C编译器是否必须将&&和||短路没有必要吗?

问题描述 投票:0回答:1

C 短路 && 和 ||。

与按位 | 不同运算符,||运算符保证从左到右评估;如果对第二操作数求值,则在第一操作数和第二操作数的求值之间存在序列点。如果第一个操作数比较不等于 0,则不计算第二个操作数。

我们经常在诸如

之类的习语中使用它

bool xyz = ptr && *ptr == 42;

但是假设我们有非常简单的 || 操作数操作员:

int a[3] = {1, 2, 3};
[ ... other code ... ]

int myFunc() {
  int x = a[0] == 1 && a[1] == 2 && a[2] == 3;
}

看起来,如果 C 必须保证后面的操作数不被求值,它就必须生成类似的东西(请原谅我随意写的记性不好的汇编伪代码):

  xor   eax, eax
  cmp   a+0x0, 0x1 ; a[0] != 1
  jne   end
  cmp   a+0x4, 0x2 ; a[1] != 2
  jne   end
  cmp   a+0x8, 0x3 ; a[2] != 3
  jne   end
  mov   al, 0x1    ; all equal, set 1
  
.end:
  ret

看起来有很多分支!我的总体直觉是不必要的分支是不好的,因为我们遇到了分支预测问题。 (过去,我用诸如

a && b
之类的语句替换了分支,认为这会删除分支!当然,我认为这在大多数情况下也使代码更清晰。)

这就是我期望的非分支版本的样子,我的直觉告诉我们在大多数情况下我们更喜欢这个。 (再次,原谅徒手的 x86):

  xor   eax, eax
  xor   ecx, ecx
  cmp   a+0x0, 0x1 ; a[0] == 1
  sete  al
  cmp   a+0x4, 0x2 ; a[1] == 2
  sete  cl
  and   al, cl
  cmp   a+0x8, 0x3 ; a[2] == 3
  sete  cl
  and   al, cl
  ret

这感觉是等价的,并且删除了分支。 (尽管将页面加载到缓存中可能已经足够“副作用”了?)

我怀疑我可以通过将我的

&&
变成
&
来鼓励编译器生成这个程序集?然而,总的来说,我的经验是我应该相信编译器能够比我更好地优化事情。

  • 如果“没有”副作用,编译器是否需要生成“短路”机器代码?
  • 有没有办法让编译器决定是使用提前退出策略还是非分支策略?
  • 在实践中,这是否会影响我在部分代码中使用 & 和 && 的方式?

相关:

c compiler-optimization short-circuiting
1个回答
0
投票

在实践中,这是否会影响我在部分代码中使用 & 和 && 的方式?

不,不应该。您应该以最清晰、最易读的形式编写代码。仅当代码分析发现性能问题时才应进行此类微观优化。

© www.soinside.com 2019 - 2024. All rights reserved.