C根据优化级别给出不同的输出(新示例)

Dav*_*545 6 c optimization gcc compilation

基于这篇非常好的博客文章,严格的别名情况非常糟糕,我已经将这段代码放在网上供您测试:

http://cpp.sh/9kht(输出在-O0和-O2之间变化)

#include <stdio.h>

long foo(int *x, long *y) {
  *x = 0;
  *y = 1;
  return *x;
}

int main(void) {
  long l;
  printf("%ld\n", foo((int *)&l, &l));
}
Run Code Online (Sandbox Code Playgroud)
  • 这里有某种未定义的行为吗?

  • 当我们选择-O2级别时,内部会发生什么?

zwo*_*wol 12

  1. 是的,这个程序有未定义的行为,因为基于类型的别名规则,可以概括为"你不能通过类型B的指针访问用类型A声明的内存位置,除非 B是指向字符类型的指针(例如unsigned char *)." 这是近似值,但它足够接近大多数用途.注意,当A是指向字符类型的指针时,B可能不是别的东西 - 是的,这意味着通过一个uint32_t*未定义的行为来访问字节缓冲区"一次四个"的常见习惯用法(博客文章也触及了这个).

  2. 编译器假定,编译时foo,这xy可能不指向同一个对象.从这一点来看,它推断写入*y不能改变其值*x,它只能返回已知值*x0,而无需从内存中重新读取它.它只在打开优化时执行此操作,因为跟踪每个指针可以和不能指向的内容是昂贵的(因此编译速度较慢).

    请注意,这是一个"恶魔飞出你的鼻子"的局面:编译有权作出生成的代码foo下手

    cmp  rx, ry
    beq  __crash_the_program
    ...
    
    Run Code Online (Sandbox Code Playgroud)

    (像UBSan这样的工具可能会这样做)

  • 实际上,`char`,`signed char`和`unsigned char`是允许访问任意类型的***不同类型. (2认同)