在 C 语言层面除了写法以外没什么区别。
int a = 5;
a == 0 ? puts("x") : puts("z");
if (a == 0) {
puts("x");
} else {
puts("z");
}
在汇编语言层面上有一些区别,if
倾向于使用条件控制转移(j
系列)命令,三元表达式倾向于使用条件数据传输(cmov
系列)命令。
void set1(int *a, int *b)
{
if (*a < *b) {
*a = *a;
} else {
*a = *b;
}
}
void set2(int *a, int *b)
{
*a = *a < *b ? *a : *b;
}
O1
优化等级编译(经测试 O2, O3
汇编代码相同),反汇编得
a in %rdi, b in %rsi
Dump of assembler code for function set1:
0x0000000000401166 <+0>: mov (%rsi),%eax
0x0000000000401168 <+2>: cmp %eax,(%rdi)
0x000000000040116a <+4>: jl 0x40116e <set1+8>
0x000000000040116c <+6>: mov %eax,(%rdi)
0x000000000040116e <+8>: retq
End of assembler dump.
Dump of assembler code for function set2:
0x000000000040116f <+0>: mov (%rdi),%eax
0x0000000000401171 <+2>: mov (%rsi),%edx
0x0000000000401173 <+4>: cmp %edx,%eax
0x0000000000401175 <+6>: cmovg %edx,%eax
0x0000000000401178 <+9>: mov %eax,(%rdi)
0x000000000040117a <+11>: retq
End of assembler dump.
在只进行简单的条件赋值时,条件数据传输命令比条件控制转移要好一些。
- C 语言层面的代码会更简洁(只要不把三元表达式写得过于复杂);
- 汇编语言层面,
j
需要进行分支预测(也就是是否要进行跳转),预测错误时会损失性能(约 \(15 \sim 30\) 个时钟周期),因为跳转意味着执行另一个地方的命令,因此cmov
性能上会更好。
当然 if
用来进行更复杂的判断和命令是更合适的。
if
、三元表达式和汇编代码之间并不是简单的一对一的关系,倾向于也只是倾向罢了,在实际操作时不必过于在意这些。