C# '|';vs'||';C语言中的编译器优化#
我最近在一次采访中被问到这个问题,我完全错了,但我对C#和.net中的编译器优化感到好奇 考虑以下代码段:C# '|';vs'||';C语言中的编译器优化#,c#,.net,optimization,compiler-construction,bitwise-or,C#,.net,Optimization,Compiler Construction,Bitwise Or,我最近在一次采访中被问到这个问题,我完全错了,但我对C#和.net中的编译器优化感到好奇 考虑以下代码段: void Main() { Console.WriteLine("Results when bitwise or is used: "); Console.WriteLine(FuncA() | FuncB()); Console.WriteLine("Results when or operator is used: "); Console.WriteL
void Main()
{
Console.WriteLine("Results when bitwise or is used: ");
Console.WriteLine(FuncA() | FuncB());
Console.WriteLine("Results when or operator is used: ");
Console.WriteLine(FuncA() || FuncB());
}
bool FuncA()
{
Console.WriteLine("Function A is executing.");
return true;
}
bool FuncB()
{
Console.WriteLine("Function B is executing.");
return false;
}
运行上述结果会得到以下结果:
使用按位or时的结果:
函数A正在执行
函数B正在执行
真的
使用or运算符时的结果:
函数A正在执行
真的
我这里的问题是,为什么编译器在使用位运算符时没有进行优化?就像对于C#or运算符,编译器已经知道了参数,那么为什么它不以同样的方式处理按位or呢 编辑:更新术语以与ECMA-334(C语言规范)保持一致。参考(如§14.10)是ECMA-334的特定章节 逻辑运算符(
|
,&
,^
)不在C中(§14.10)。这意味着即使表达式的最终结果由操作数的子集唯一确定,也将对所有操作数求值。语言规范中没有明确说明这些运算符不是短路的事实,但签名的表示方式暗示了这一点。对这些运算符的操作数进行求值时,就像在方法调用中传递操作数一样,即从左到右,而不是短路
bool operator &(bool x, bool y);
bool operator |(bool x, bool y);
bool operator ^(bool x, bool y);
另一方面,条件逻辑运算符(|
,&&
)是短路(§14.11)
该行为在语言规范(标准)中定义,可能是因为其与早期编程语言(C++,Java)的一致性而被选择的。这不是优化-它是
&&
和|
操作符的基本属性,称为短路
短路背后的理论是,左操作数的某些值意味着我们不必测试右操作数。使用&&&
,当左侧为false时,表达式作为一个整体不可能为true,因此计算右侧没有意义。对于|
,如果左侧为真,则右侧不相关
位运算符
|
,&
,^
不会短路-必须计算它们的两个操作数才能确定结果。位运算符不会优化,因为它在需要结果的整个宽度时使用。本质上,它被定义为不被优化
相反,定义OR运算符是为了确定任一操作数是否为真
另一种考虑是OR运算符(>)是<强>布尔-< /强>运算符,它是可优化的,而位运算符(<)是强>不< /强>布尔运算符,因此不能短路。
运营商和&
和| |
使用提前退出策略,专门防止右项的副作用
bool operator &(bool x, bool y);
bool operator |(bool x, bool y);
bool operator ^(bool x, bool y);
Wikipedia 280Z28页链接的这个(稍加修改的)C示例显示了一个有用的示例:
int denom = 0;
if (denom && num/denom) {
do_something();
}
在这种情况下,尽早退出术语评估意味着程序在评估条件时不会抛出除零错误,这是一件好事
相反,语言规范定义了位运算符,以在对其执行操作之前评估这两个术语,包括所有副作用
一个考虑这一点的方法是考虑比特运算符为<强> EXPRESS < /强>和逻辑运算符为<强>懒惰>强>关于它们的评价。渴望操作符将在继续之前评估它们的所有操作数,懒惰操作符按顺序考虑操作数,如果可能的话尽早退出。
顺便说一句,(假设的)
^
运算符不能在不计算两个操作数的情况下退出,因为结果不能由一个操作数的特定值确定,就像&
和|检查语言规范的情况一样。。。这不是一个真正的问题,因为该代码中没有涉及优化,但明确指定了行为(对|
短路,对|
两侧都必须进行计算)。有一秒钟,我认为C/C++中确实存在冗余的^
运算符。。因为嘿,这不是语言中最奇怪的事情;是的,我知道。但它应该在那里:PHeck,如果这是C++14的一个补充,我不会感到惊讶;)严格地说,如果|的左操作数都是1位的,那么编译器就不必计算另一端,但它需要计算,因为这是语言定义的方式。