可以优化 b&&0

问题描述

我知道 C/C++ 使用短路评估来评估布尔表达式。例如,C/C++肯定会在表达式a中的操作数b之前计算操作数a && b,如果a为假,b不会进行评估。

另外,我知道像 5==6 这样的东西可能会被编译器完全忽略,因为它是一个常量表达式,可以在编译时计算。

但是不知道编译器能不能优化b && 0?编译器可以说:好吧,0的求值比b的求值容易很多,而且b没有任何副作用,所以我决定改b && 0进入 0 && b 以先评估 0

解决方法

您的问题涉及两个独立的问题。第一个是当编译器“看到”if 条件总是false(由于&& 0)时,它可以完全丢弃相应的分支。示例翻译单元:

bool f(int);

int main()
{
  if (f(1) && 0)
    return 1;
}

启用优化后,很可能不会为分支生成机器代码。但是,f(1) 表达式仍必须在运行时求值,因为编译器无法证明 f(1) 调用没有可观察到的行为。

机器代码:https://godbolt.org/z/sEMrfh

相反,如果编译器可以证明 f(1) 没有可观察的行为,它可以消除它的调用。这与求值顺序无关,而是与 as-if 规则有关。演示翻译单元:

static bool f(int i)
{
  int j = i + 1;
  return true;
}

int main()
{
  if (f(1) && 0)
    return 1;
}

机器代码:https://godbolt.org/z/scs3je

,

&&|| 运算符保证从左到右求值。评估意味着编译器必须检查操作数的副作用,如果存在副作用,它必须执行这些。如果 && 的左操作数计算为零,则不会计算右操作数。

考虑if(func() && 0) { do_stuff(); }。尽管 && 表达式永远不可能为真,但仍必须执行该函数。编译器不会做一些奇怪的重新排序,比如 0 && func(),它只会用 func(); 替换整个表达式,同时删除 ifdo_stuff() .

一般情况下,编译器特别不允许对&&||的操作数的求值或执行重新排序;它们在左操作数和右操作数的计算之间有一个所谓的序列点。这反过来又允许像 (ptr=malloc(...)) && (*ptr = x) 这样的代码被很好地定义并且在 malloc 失败时不会访问空指针。