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

C/C++ Indeterminate Values: Compiler optimization gives different output (example)

本文关键字:输出 示例 不确定 C++ 优化 编译器      更新时间:2023-10-16

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

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

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

#include <stdio.h>
#include <stdlib.h>
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. 为什么 no-optimization 的输出为 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++语言不能保证这一点。

为什么 no-optimization 的输出为 14,而 -O3 的输出为 10

因为当您取消引用无效指针时,行为是未定义的。


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

使用指向生存期已结束的对象的指针的值。

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

确定执行将始终进入 if 语句吗?我们怎么知道两个指针 p 和 q 的地址会相同?

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

为什么 no-optimization 的输出为 14,而 -O3 的输出为 10

这就是优化器的工作方式,您可以在此处查看您的版本:

https://goo.gl/yRfjIv

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

https://goo.gl/vSVV0E

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

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

[编辑]

未定义的行为是由编译器假定不再有效的指针值的使用引起的,它是否稍后等于某些新分配的内存块并不重要。适当的标准报价由TartanLlama给出:

[基本.stc.dynamic.safety]

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

if -条件可能是假的 - 根据malloc()的特定实现,它可能会返回刚刚释放的块以供重用或另一个块。

但是,如果程序打印任何内容(因为碰巧q等于 p ),它必须打印14 。产生其他任何东西的编译器都是有缺陷的......

在这里使用 clang 3.4.1 和

3.6.2,我始终得到正确的答案,而 gcc 4.2.1 和 5.3.0 都表现出该错误。不幸的是,clang 3.8.0也是如此。