C / C ++不确定值:编译器优化提供不同的输出(示例)

看起来C / C ++编译器(clang,gcc等)产生与优化级别相关的不同输出。 您也可以查看这篇文章中包含的在线链接。

http://cpp.sh/5vrmv (将输出从none更改为-O3以查看差异)。

根据以下代码,有人可以解释我的一些问题:

#include  #include  int main(void) { int *p = (int *)malloc(sizeof(int)); free(p); int *q = (int *)malloc(sizeof(int)); if (p == q) { *p = 10; *q = 14; printf("%d", *p); } return 0; } 
  1. 是否确定执行将始终进入if语句? 我们怎么知道两个指针p和q的地址是一样的?
  2. 为什么没有优化有输出14 ,而-O3有相同指令的输出10

 free(p); 

这会将p的内容转换为无效的指针值。

 int *q = (int *)malloc(sizeof(int)); 

这一行与p无关。

 if (p == q) { 

这是实现定义的行为,因为p具有无效的指针值。

  *p = 10; 

最后,由于与上述相同的原因,这是未定义的行为。

C ++标准§3.7.4.2/ 4:

如果给标准库中的释放函数赋予的参数是一个不是空指针值的指针(4.10),则释放函数将释放指针引用的存储, 使得所有指针都无效 ,引用解除分配的存储的任何部分。 通过无效指针值间接并将无效指针值传递给释放函数具有未定义的行为 。 对无效指针值的任何其他使用都具有实现定义的行为。

因此,您的问题的答案是:

是否确定执行将始终进入if语句?

这取决于实施。 C ++语言并不能保证它。

为什么没有优化有输出14,而-O3有相同指令的输出10?

因为取消引用无效指针时行为未定义。


在C中,比较本身是未定义的行为。 C标准中的附录J.2列出了未定义行为的情况,该列表包括:

使用指向生命周期结束的对象的指针的值。

您可能会发现以下问题,包括所有有趣的评论和答案: 未定义,未指定和实现定义的行为

是否确定执行将始终进入if语句? 我们怎么知道两个指针p和q的地址是一样的?

这是实现定义的,您不能依赖此行为。 pq确实可以相等,你释放了p指向的内存,所以q可能得到与p相同的地址。

为什么没有优化有输出14,而-O3有相同指令的输出10?

这是优化器的工作原理,你可以在这里看到你的版本:

https://goo.gl/yRfjIv

编译器优化14的赋值,这里看起来正确的版本:

https://goo.gl/vSVV0E

正在分配值14,我只添加了一行p = q;

我不确定为什么它会像那样工作,我会说编译器假定您的代码没有未定义的行为代码,并在这种假设下进行优化。

[编辑]

未定义行为是由使用指针值引起的,而编译器假定该值不再有效,如果它稍后等于某个新分配的内存块则无关紧要。 TartanLlama给出了适当的标准引用:

[basic.stc.dynamic.safety]

[注意:使用无效指针值(包括将其传递给释放函数)的效果未定义,请参见3.7.4.2。 即使不安全派生的指针值可能比较某些安全派生的指针值,也是如此。 – 尾注]

if -condition可能是false – 取决于malloc()的特定实现,它可能返回刚刚释放的块以供重用或不同的块。

但是,如果程序打印任何东西 (因为它发生了q等于p ),它必须打印14 。 生成其他任何东西的编译器都是错误的……

在这里使用clang 3.4.1和3.6.2我一直得到正确答案,而gcc 4.2.1和5.3.0都表明了这个bug。 不幸的是,clang 3.8.0也是如此。