【问题标题】:C++ evaluation order and sequenced before and compiler reorder?C ++评估顺序和之前排序以及编译器重新排序?
【发布时间】:2018-11-09 10:05:07
【问题描述】:

我读过Order of evaluation,但不是很明白。顺序是指运行时的执行顺序还是源代码中的逻辑顺序?

让我们看一个代码sn-p如下:

void f()
{
   int a = 10; // A
   int b = 20; // B
   //...
}

这是否意味着表达式A 在表达式B 之前排序?

c++ 编译器是否允许重新排序代码如下?

void f()
{
   int b = 20; // B
   int a = 10; // A
    //...
}

如果编译器确实像上面那样对代码重新排序,那么我们是否应该说表达式BA 之前排序?

【问题讨论】:

  • 排序是关于运行时的评估顺序。只要不破坏排序规则,编译器就可以重新排序任何东西。这些规则也适用于 表达式 而不是语句。至于您的示例,它与排序规则无关,因为它是两个语句,并且编译器可以根据需要对其进行重新排序,因为它是两个独立语句。如果你有例如int b = a; 则无法重新排序。
  • @Someprogrammerdude 如果我有 int a = x + 1; int b=y+1;?
  • 那仍然是两个独立的语句,彼此没有依赖关系。并且编译器可以以任何它喜欢的方式重新排序这些初始化。
  • 另请阅读Undefined behavior and sequence points。它可以帮助您更好地理解这些事情。
  • 而且很多现代 CPU 都出现故障了,所以它们也可以在运行中重新组织指令,只要它们之间没有依赖关系。

标签: c++ c++11


【解决方案1】:

作为一般规则,允许编译器做任何事情,只要结果与编译您的代码完全一样。

事实上,你的典型 C/C++ 编译器,启用任何合理的优化标志,给定如下函数:

void f (void) {
  int a = 20;
  int b = 10;
}

将简单地编译为:

f:
  ret

换句话说,它将把它当作一个空函数。原因是该功能没有任何效果。这些变量被赋值,但它们的值从未被使用(在技术术语中,它们是死存储),因此编译器可以忽略它们。

现在,让我们看一个更实际的例子:

void foo (int num, int * num2, int * num3) {
  *num2 = num * 2;
  *num3 = num * 3;
}

编译器可以重新排序这些语句吗?答案是肯定的。原因是num2num3 可能指向同一个地址(换句话说,你可以调用像foo(3, &bar, &bar) 这样的函数),因此写入的顺序很重要。另一方面,仅在 C(但不是 C++)中,我们可以这样写:

void foo (int num, int * restrict num2, int * restrict num3) {
  *num2 = num * 2;
  *num3 = num * 3;
}

在这种情况下,restrict 关键字告诉编译器指针必须指向不同的地址,因此允许对语句重新排序,因为无论执行顺序如何,结果都是相同的。

【讨论】:

  • 感谢您的解释!那么编译器重新排序与评估顺序无关?
  • 为什么foo 的顺序很重要?您正在用独立值覆盖变量
  • @Fureeish 因为num2num3 可以指向同一个地方(它们是指针),正如我在将其称为foo(3, &bar, &bar) 的示例中所展示的那样——bar 首先会变成6 然后 9,但它必须按照这个顺序发生,因为最终结果必须是 9。在最后一个示例中(仅在 C 中有效,因为 C++ 没有restrict),我明确地说指针将指向不同的地方(因为这就是 restrict 的意思),这使它像你说的那样工作 - 因此它可以在这种情况下进行优化。
  • 啊,有道理。我不知何故迷路了:>
  • @ricky 我不知何故从未看到你的评论,对不起!求值顺序是求值表达式的理想顺序;编译器的行为必须好像它们是按该顺序评估的。顾名思义,重新排序是编译器以它喜欢的任何顺序评估表达式的能力(通常出于性能原因),只要结果和最终效果符合要求,就可以进行必要的调整相同。
猜你喜欢
  • 2012-02-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多