📜  C中严格的混叠规则和示例

📅  最后修改于: 2021-05-25 22:16:42             🧑  作者: Mango

别名:别名是指可以使用不同名称访问相同内存位置的情况。

例如,如果一个函数两个具有相同值的指针AB ,则名称A [0]为名称B [0]的别名,即,我们说指针AB为彼此的别名。

下面是说明C语言中别名的程序:

C
// C program to illustrate aliasing
#include 
  
// Function to add 10 to b
int gfg(int* a, int* b)
{
    *b = *b + 10;
    return *a;
}
  
// Driver Code
int main()
{
    // Given data
    int data = 20;
  
    // Function Call with aliasing
    int result = gfg(&data, &data);
  
    // Print the data
    printf("%d ", result);
}


C
// C program to illustrate aliasing
#include 
  
// Function to change the value of
// ptr1 and ptr2
int foo(int* ptr1, int* ptr2)
{
    *ptr1 = 10;
    *ptr2 = 11;
    return *ptr1;
}
  
// Driver Code
int main()
{
    int data1 = 10, data2 = 20;
  
    // Function Call
    int result = foo(&data1, &data2);
  
    // Print result
    printf("%d ", result);
    return 0;
}


C
#include 
  
int foo(int* ptr1, long* ptr2)
{
    *ptr1 = 10;
    *ptr2 = 21.02;
    return *ptr1;
}
  
int main()
{
    int a = 11;
    long b = 20.02;
    int result = foo(&a, &b);
    printf("%d ", result);
    return 0;
}


C
#include 
  
int foo(int* ptr1, long* ptr2)
{
    *ptr1 = 10;
    *ptr2 = 11;
    return *ptr1;
}
  
int main()
{
    long a = 11.0;
    int result = foo((int*)&a, &a);
    printf("%d ", result);
    return 0;
}


CPP
#include 
using namespace std;
  
__uint32_t left(__uint32_t val)
{
    __uint32_t val_temp = val;
    // can't use static_cast<>,
    // not legal
    __uint16_t* ptr
        = (__uint16_t*)&val_temp;
    __uint16_t tmp = ptr[0];
    ptr[1] = ptr[0];
    ptr[0] = 0;
    return val_temp;
}
  
int main()
{
    __uint32_t val;
    val = 1;
    val = left(val);
    cout << val << endl;
}


输出:
30

解释:
在上面的程序中,data的值为20。当将此变量函数gfg()的别名时,指针“ b”所指向的内存位置所做的更改也将反映在“ a”中。这样做是因为它们引用了相同的内存位置。

严格的混叠:
GCC编译器假设不同类型的指针永远不会指向相同的内存位置,即彼此的别名。严格的别名规则有助于编译器优化代码。默认情况下,在优化级别2和更高级别上会启用“严格别名”规则,并且当人们尝试编译具有上述详细信息的程序时,编译器会发出警告,尽管该程序仍可以编译并可以执行,但该程序的输出仍然不可预测。

C

// C program to illustrate aliasing
#include 
  
// Function to change the value of
// ptr1 and ptr2
int foo(int* ptr1, int* ptr2)
{
    *ptr1 = 10;
    *ptr2 = 11;
    return *ptr1;
}
  
// Driver Code
int main()
{
    int data1 = 10, data2 = 20;
  
    // Function Call
    int result = foo(&data1, &data2);
  
    // Print result
    printf("%d ", result);
    return 0;
}
输出:
10

解释:
在上面的代码中,编译器无法优化代码以直接返回10,因为指针ptr1和ptr2可以互为别名。在这种情况下,它将返回11,而不是10。

即使我们确定两个指针不是彼此的别名,编译器也无法执行优化。现在,为了解决这个问题,引入了严格的混叠规则。它可确保编译器确保不同类型的指针不能彼此成为别名。

C

#include 
  
int foo(int* ptr1, long* ptr2)
{
    *ptr1 = 10;
    *ptr2 = 21.02;
    return *ptr1;
}
  
int main()
{
    int a = 11;
    long b = 20.02;
    int result = foo(&a, &b);
    printf("%d ", result);
    return 0;
}
输出:
10

由于编译器确定两个指针都不互为别名,因此可以直接对代码进行直接优化以返回10。在这里,我们可以观察到编译器可以利用严格的混叠规则来生成优化程度更高的代码。

注意:由于C和C++都允许在指针类型之间进行强制转换,这最终将创建别名,因此违反了编译器的假设。

C

#include 
  
int foo(int* ptr1, long* ptr2)
{
    *ptr1 = 10;
    *ptr2 = 11;
    return *ptr1;
}
  
int main()
{
    long a = 11.0;
    int result = foo((int*)&a, &a);
    printf("%d ", result);
    return 0;
}
输出:
11

另一个违反严格别名规则的示例程序:

CPP

#include 
using namespace std;
  
__uint32_t left(__uint32_t val)
{
    __uint32_t val_temp = val;
    // can't use static_cast<>,
    // not legal
    __uint16_t* ptr
        = (__uint16_t*)&val_temp;
    __uint16_t tmp = ptr[0];
    ptr[1] = ptr[0];
    ptr[0] = 0;
    return val_temp;
}
  
int main()
{
    __uint32_t val;
    val = 1;
    val = left(val);
    cout << val << endl;
}
输出:
65536

解释:

上面的代码通过将低16位交换为高16位并将高16位设置为0,将变量val的值左移了16位,但是这里我们使用__uint16_t指针ptr创建了“ val”的别名。 ,这违反了严格的别名规则,并且可能导致编译器在不同级别的优化(例如O1,O2和O3)上产生不同的输出。

如何绕过严格的别名规则?:

尽管已实施规则以防止意外行为,但是可以通过忽略警告并在编译时显式使用-fno-strict-aliasing标记来绕过规则,这即使在应用O3,O2级优化时也可以确保不强制执行任何规则。

想要从精选的最佳视频中学习和练习问题,请查看《基础知识到高级C的C基础课程》。