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
这感觉是等价的,并且删除了分支。 (尽管将页面加载到缓存中可能已经足够“副作用”了?)
我怀疑我可以通过将我的
&&
变成&
来鼓励编译器生成这个程序集?然而,总的来说,我的经验是我应该相信编译器能够比我更好地优化事情。
相关:
在实践中,这是否会影响我在部分代码中使用 & 和 && 的方式?
不,不应该。您应该以最清晰、最易读的形式编写代码。仅当代码分析发现性能问题时才应进行此类微观优化。