这两种情况很可能会产生完全相同的机器代码,或者非常接近的代码。所以选择哪一个是编码风格的问题。
我想说,如果操作很复杂,以至于它们不容易放在一行上,那么if-else if 是首选。否则,如果操作有些简单,可能更喜欢|| 版本。虽然这是相当主观的。
关于这些运算符的内部工作原理的高级讨论如下。如果你对这些东西不是很感兴趣,你可以停止阅读这里!
然而,有一些细节使这两个版本略有不同。在这两种情况下,每个单独的操作数都使用类型平衡隐式提升。但在 || 的情况下,发生了额外的隐式提升:
(!x && y && z) 的结果与(x && !y && !z) 的结果相平衡。最后,结果是转换等级最大的操作数类型。
这有关系吗?我看不出在这种特定情况下会如何。但是假设我们有这样的东西:
uint8_t x;
uint8_t y;
uint32_t z;
然后我们有一个表达式:
if( (x && y) || (x && z) )
// or
if(x && y)
{}
else if(x && z)
{}
假设我们使用的是 8 位或 16 位 CPU,其中int 是 16 位。
在 if-else 情况下,会发生以下情况:
- x 和 y 都是从
uint8_t 提升到 int 的整数,因为它们是小整数类型。
- 表达式
(int)x && (int)y 是平衡的。 x && y 是根据 int 类型计算的,结果是 int 类型。
- 在表达式
x && z中,x是上面提升的整数,但不是z,它不是小整数类型,仍然是uint32_t。
- 表达式
(int)x && (uint32_t)z 是平衡的,然后在uint32_t 类型上计算。结果是uint32_t 类型。
- 因此,
x && y 始终在 16 位变量上计算,x && z 始终在 32 位变量上计算。
如果我们查看|| 版本,也会发生同样的事情。但是我们还有另一个额外的平衡:(uint16_t)first_result || (uint32_t)second_result。由于|| 运算符,这种平衡是强制执行的。这意味着如果x==true 和y==true,那么整个操作的结果将是uint32_t 类型。
但在 if-else 版本中,x && y 类型始终为 uint16_t。 || 版本在 x 类型和 z 类型之间引入了一种模糊的紧密耦合。
编译器是否能够有效地优化|| 场景,我不知道。我希望它会,但是由于编译器无法对整个表达式的结果做出任何编译时决定,因为它不知道操作数是真还是假,它可能最终只是执行它在 32 位类型上。如果我们有一个 8 位或 16 位 CPU,这将是一个坏消息,这将非常低效地处理 32 位数字。