【问题标题】:C/C++ Indeterminate Values: Compiler optimization gives different output (example)C/C++ 不确定值:编译器优化给出不同的输出(示例)
【发布时间】:2016-06-15 03:57:31
【问题描述】:

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

http://cpp.sh/5vrmv(将输出从无更改为 -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. 为什么同样的指令无优化输出14,而-O3输出10

【问题讨论】:

  • 至少在这里*p = 10; 你有未定义的行为。不,不能保证malloc() 会重复使用释放的地址。
  • “但是为什么两个不同的优化级别之间是一致的?” 未定义的行为意味着任何事情都可能发生,包括你的观察。
  • @MikhailT.:== 的结果是实现定义的。没有什么能阻止实现返回true,然后仍然将两者视为不同。
  • @MikhailT。标准中甚至有一条注释明确解决了这个问题:[basic.stc.dynamic.safety]: "[注意:使用无效指针值(包括将其传递给释放函数)的效果是未定义的。即使不安全派生的指针值也是如此。可能比较等于某个安全派生的指针值。—end note ]"
  • @TartanLlama 不,因为不确定的值可能是陷阱表示,访问它是未定义的行为。 (对于 C 来说也是如此)

标签: c++ c optimization compilation compiler-optimization


【解决方案1】:
free(p);

这会将p 的内容变成无效的指针值。

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

此行与p 无关。

if (p == q) {

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

    *p = 10;

最后,这是 undefined 行为,原因与上述相同。

C++ 标准§3.7.4.2/4:

如果给标准中的释放函数的参数 library 是一个不是空指针值的指针(4.10), 释放函数应释放由 指针,呈现无效所有指针指向的任何部分 释放的存储空间。通过无效的指针值间接和 将无效指针值传递给释放函数 未定义的行为。无效指针值的任何其他用途 实现定义的行为。

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

确定执行总是会进入if语句吗?

这取决于实现。 C++语言不保证。

为什么同样的指令无优化输出14,而-O3输出10?

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


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

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

您可能会发现以下问题(包括所有 cmets 和答案)很有趣:Undefined, unspecified and implementation-defined behavior

【讨论】:

  • 比较是 C 中未定义的行为。请参阅:stackoverflow.com/questions/26704344/…
  • @2501 只是为了澄清:(渲染的)无效指针具有“不确定值”,对吗?并且访问一个不确定的值总是会导致未定义的行为?
  • @2501: 我为 C 添加了一些东西。
  • @ChristianHackl 您如何描述无效指针的 ?不确定还是未指定?
  • @Dave5545 在这种情况下,未定义的行为发生在编译时。由于 UB 的定义,允许编译器将 if 语句默认为 false..
【解决方案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。即使不安全派生的指针值可能 比较等于某个安全派生的指针值。 ——尾注]

【讨论】:

  • @Dave5545 因为标准允许它这样做,它可能更容易实现一些优化。
  • 所以,我猜,悬空指针可以验证 if 语句,但在任何时候被尊重时仍然会导致未定义的行为。
  • 比较是 C 中未定义的行为。请参阅:stackoverflow.com/questions/26704344/…
  • @2501 标准对此进行了说明:[basic.stc.dynamic.safety]:38) Some implementations might define that copying an invalid pointer value causes a system-generated runtime fault.
  • @marcinj 引用不是来自 C(我很确定)。请参阅链接上的标签和我的评论。这个问题(遗憾地)被标记为两者。
【解决方案3】:

if-条件可能为假——取决于malloc() 的特定实现,它可能返回刚刚释放的块以供重用或其他块。

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

在这里使用 clang 3.4.1 和 3.6.2 我始终得到正确答案,而 gcc 4.2.1 和 5.3.0 都显示了该错误。不幸的是,clang 3.8.0 也是如此。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-07-24
    • 1970-01-01
    • 2021-06-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多