2

我最近在一次采访中被问到这个问题,我完全错了,但让我对 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.WriteLine(FuncA() || FuncB()); 
}

bool FuncA()
{
    Console.WriteLine("Function A is executing.");
    return true;
}

bool FuncB()
{
    Console.WriteLine("Function B is executing.");
    return false;
}

运行上述结果得到以下结果:

使用按位或时的结果:

函数 A 正在执行。

功能 B 正在执行。

真的

使用 or 运算符时的结果:

函数 A 正在执行。

真的

我的问题是为什么编译器在使用按位运算符时没有优化?就像 C# 或运算符一样,编译器已经知道参数,那么为什么它不以与按位或相同的方式处理它呢?

4

4 回答 4

13

编辑:更新术语以与 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)。&&

该行为在语言规范(标准ECMA-334)中定义,并且可能因其与早期编程语言(C++、Java)的一致性而被选中。

于 2013-05-01T01:32:57.933 回答
5

这不是优化 - 它是&&and||运算符的基本属性,称为短路

短路背后的理论是左操作数的某些值意味着我们不必测试右操作数。有了&&,当左侧为假时,整个表达式不可能为真,因此评估右侧没有意义。对于||,当左侧为真时,右侧无关紧要。

位运算符|, &,^不会短路 - 必须评估它们的两个操作数才能确定结果。

于 2013-05-01T01:34:11.010 回答
4

位运算符不会优化,因为它在需要结果的整个宽度时使用。本质上,它被定义为不被优化。

相反,OR 运算符被定义为确定一操作数是否为真。

另一种考虑方式是 OR 运算符 (||) 是布尔运算符,可优化,而按位运算符 (|)不是布尔运算符,因此不能短路。

于 2013-05-01T01:34:56.140 回答
2

和运算&&||专门使用提前退出策略来防止右手项的副作用。

来自维基百科页面 280Z28 链接的这个(稍微修改过的)C 示例显示了一个有用的示例:

int denom = 0;
if (denom && num/denom) {
    do_something();
}

在这种情况下,提前退出条款的评估意味着程序在评估条件时不会抛出被零除的错误,这是一件好事。

相反,按位运算符由语言规范定义,以在对它们执行操作之前评估这两个术语,包括所有副作用。

考虑这一点的一种方法是将按位运算符视为急切,而将逻辑运算符视为懒惰。急切的运算符将在继续之前评估其所有操作数,惰性运算符将按顺序考虑操作数并在可能的情况下提前退出。

顺便说一句,(假设的)^^运算符不能在不评估两个操作数的情况下退出,因为结果不能由一个操作数的特定值确定,就像&&and的情况一样||

于 2013-05-01T01:53:06.630 回答