【问题标题】:Different behavior of shift operator with -O2 and without带 -O2 和不带 -O2 的移位运算符的不同行为
【发布时间】:2011-03-04 15:29:06
【问题描述】:

如果没有 -O2,此代码将打印 84 84,如果带有 O2 标志,则输出为 84 42。该代码是在 64 位 Linux 平台上使用 gcc 4.4.3. 编译的。为什么以下代码的输出不同?

请注意,使用 -Os 编译时,输出为 0 42

#include <iostream>
using namespace std;

int main() {
    long long n = 42;
    int *p = (int *)&n;
    *p <<= 1;
    cout << *p << " " << n << endl;
    return 0;
}

【问题讨论】:

  • g++ 4.5.2 在 x86_64 linux 上使用 -O0、-O1、-O2、-O3 和 -Os 打印 84 84
  • g++ 4.5.0 在 64 位 Windows 上以任何优化级别打印 84 84
  • 我相信int *p = (int *)&amp;n;这行的结果取决于机器的字节序!
  • 你为什么使用int*long long
  • 我能够重现 g++ 4.4.4 的问题。看起来 4.4 分支有一个在 4.5 中修复的错误/功能。

标签: c++ gcc 64-bit compiler-optimization strict-aliasing


【解决方案1】:

当您使用 gcc 进行优化时,它可以根据表达式的类型使用某些假设,以避免重复不必要的读取并允许在内存中保留变量。

您的代码具有未定义的行为,因为您将指向 long long(gcc 允许作为扩展)的指针转换为指向 int 的指针,然后将指向的对象当作 @987654324 来操作@。指向int 的指针通常不能指向long long 类型的对象,因此允许gcc 假定写入int 的操作(通过指针)不会影响具有@ 类型的对象987654328@.

因此,在 n 的初始分配时间和随后打印的时间之间缓存它是合法的。没有有效的写操作可以改变它的值。

要阅读的特定开关和文档是-fstrict-aliasing

【讨论】:

  • Beat me to it :) Wikipedia 给出了一些解释:en.wikipedia.org/wiki/Pointer_aliasing where one can read 在 C++ 中,如果指针参数指向根本不同的类型(“严格别名”规则)。这允许进行比 C 中更多的优化。 请注意,char* 有一个特殊规则,允许为任何指针起别名。
  • @Matthieu:Wikipedia 上关于“比 C 进行更多优化”的内容是什么? C 也有严格的别名规则(自 C99 起),但老实说,我从来没有真正与 C++ 并排比较以检查它们是否相同。如果我正在做一些边缘性的事情,我要么(a)不做,要么(b)成为我正在做的语言规则的临时专家,然后很快又忘记了确切的细节。跨度>
  • @Steve:实际上,如果你看到这篇文章,他们已经区分了 C 和 C99 (restrict)。由于我不是 C 专家(C++ 对我的大脑来说已经够难了),不过我很难回答你的问题!
  • @Matthieu:啊,对,所以维基百科的文章只是比维基百科的时代更过时。发生;-)
【解决方案2】:

你打破了严格的别名。使用 -Wall 编译应该会给你一个dereferencing type-punned pointer 警告。参见例如http://cellperformance.beyond3d.com/articles/2006/06/understanding-strict-aliasing.html

【讨论】:

    【解决方案3】:

    我在 Linux/i386 上使用 GCC 4.4.4 得到了相同的结果。

    程序的行为未定义,因为它违反了严格的别名规则。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-05-23
      • 2021-11-17
      • 2017-08-10
      • 2012-03-09
      • 2016-12-24
      • 2016-09-29
      • 1970-01-01
      • 2019-07-30
      相关资源
      最近更新 更多