c - 是否有现代目标/编译器的任何示例,其中打破 C 中的严格别名会影响程序结果?

标签 c strict-aliasing

有时关于 Stack overflow 的问题和答案建议将指针转换作为一种有效的类型双关方式。它经常被声称这会破坏严格的别名并因此调用未定义行为的说法所拒绝。

现代目标真的关心严格别名吗?在这种情况下,是否有任何程序实例会显示意外行为?

最佳答案

是的,严格别名是一种非常真实的现象,现代编译器经常利用它来执行优化。

考虑以下代码 -

typedef struct
{
    char a;
} my_struct;

void foo( int * a, my_struct * b, int count )
{
    int i;

    for (i=0;i<count;i++)
    {
        a[i] += b->a;
    }
}

当使用 clang 3.8.0-2(这是一个非常现代的编译器)为 X64 目标(这也是一个非常现代的目标)使用命令编译时 -

clang -m64 -S -O2 -std=c11 foo.c -fno-vectorize -fno-unroll-loops

生成以下程序集(简化并采用 AT&T 语法)-

foo:
    testl   %edx, %edx
    jle     .LBB0_3
    movsbl  (%rsi), %eax   # Value loaded only once before start of loop
    .align  16, 0x90
.LBB0_2:
    addl    %eax, (%rdi)
    addq    $4, %rdi
    decl    %edx
    jne     .LBB0_2
.LBB0_3:
    retq

可以看到 b->a 的值在循环开始之前只加载一次,并添加到 a 中的所有整数。

但是如果这个函数被调用为 -

my_struct a[100];
// initializaion of values in a;
...

foo((int*)a, a, 2);  // Breaking strict aliasing

现在很容易看出结果与预期不符。

在使用-fno-strict-aliasing 的较低优化级别,编译器在内部添加指令movsbl (%rsi), %eax循环体。

因此可以公平地得出结论,具有现代编译器的现代架构确实利用了严格的别名,因此不能将指针转换用作类型双关的一种方式。

引用资料: 该示例的灵感来自于此 blog post

关于c - 是否有现代目标/编译器的任何示例,其中打破 C 中的严格别名会影响程序结果?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50391984/

相关文章:

java - 如何伪装窃听接口(interface)流量

c++ - 使用 n[c -'0' ] 的真正用途是什么?

c++ - MSVC++ 限制关键字和局部变量

c - 这种 union 的使用是否严格符合要求?

c++ - uint32_t 和 uint8_t[4] 未定义行为的 union ?

c - C中这个奇怪的函数定义语法是什么?

c - 使用周期计数计算吞吐量

c - 分支优化

c - 使用类型双关将对象分解为单词

c - 将 char * 类型转换为 int *