在 C/C++ 中,在不使用计算的布尔值的情况下使用布尔运算符短路进行控制流是否安全?

dat*_*olf 3 c c++ language-lawyer

一段继承代码中的重复模式是,只要其中一个函数返回某个值,就会调用函数链并中止该链。该值随后将用于后续计算。为了演示起见,破坏值为0。考虑以下程序:

#include <stdio.h>

static int n;

static int foo(int x)
{
    fprintf(stderr, "%d(%d) ", x, n++);
    return x ? 0 : n;
}

static void with_if(void)
{
    int rc;
    n = 0;

    do {
        if( (rc = foo(1)) ) break;
        if( (rc = foo(2)) ) break;
        if( (rc = foo(3)) ) break;
        if( (rc = foo(4)) ) break;
        if( (rc = foo(0)) ) break;
        if( (rc = foo(5)) ) break;
    } while(0);

    fprintf(stderr, ">>%d<<\n", rc);
}

void with_short_circuit(void)
{
    int rc;
    n = 0;

       (rc = foo(1))
    || (rc = foo(2))
    || (rc = foo(3))
    || (rc = foo(4))
    || (rc = foo(0))
    || (rc = foo(5));

    fprintf(stderr, ">>%d<<\n", rc);
}

int main(int argc, char *argv[])
{
    with_if();
    with_short_circuit();
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

请注意,短路变体不仅更简洁,而且(恕我直言)也更容易阅读和推理,因为在阅读该代码时,您不必将所有其他周围的语句推入您的思维堆栈中。所以我大体上更喜欢短路的变体。

就 GCC-4.9.3 和 Clang-3.6.2 而言with_ifwith_short_circuit它们是相同的(它们产生完全相同的汇编输出)。

我担心的是,布尔运算符链的结果会被忽略(如果使用-WallGCC 编译会发出警告,但 Clang 仍然保持沉默)并且这可能被视为优化的机会。当然,调用 foo 会产生副作用,因此在我对 C 语言标准的理解中,使用布尔短路进行这样的控制流应该是安全的。有点我不太确定。

ala*_*ain 5

这称为“丢弃值表达式”,必须对其进行评估:

N4296,5§11:

在某些情况下,表达式仅因其副作用而出现。这样的表达式称为丢弃值表达式。计算表达式并丢弃其值。