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

时间:2016-03-15 16:32:21

标签: c gcc optimization compilation

基于这篇非常好的博客文章The Strict Aliasing Situation is Pretty Bad,我已经在网上发布了一段代码供您测试:

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));
}
  • 这里有某种未定义的行为吗?

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

2 个答案:

答案 0 :(得分:12)

  1. 是的,此程序具有未定义的行为,因为基于类型的别名规则,可以概括为&#34;您无法访问通过类型A声明的内存位置当B是指向字符类型的指针(例如unsigned char *)时,类型为B的指针之外。&#34;这是近似值,但它足够接近大多数用途。请注意,当 A 是指向字符类型的指针时,B可能是别的 - 是的,这意味着访问字节缓冲区的常用习语&#34;四在一个时间&#34;通过uint32_t*是未定义的行为(博客文章也涉及到这一点)。

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

    请注意,这是一个&#34;恶魔飞出你的鼻子&#34;情况:编译器有权使foo的生成代码以

    开头
    cmp  rx, ry
    beq  __crash_the_program
    ...
    

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

答案 1 :(得分:1)

换句话说,代码(int *)&l表示将指针视为指向int的指针。它没有转换任何东西。因此,(int *)告诉编译器允许您将long *传递给期望int *的函数。你骗了它。在里面,foo期望x是指向int的指针,但事实并非如此。内存布局不是应该的。如你所见,结果是不可预测的。

另一方面,我不会使用l(ell)作为变量名。它太容易与1(一)混淆。例如,这是什么?

int x = l;