条件运算符的评估顺序

Order of evaluation for conditional operator

已知赋值运算符=和条件运算符?:都具有右结合性。在以下代码示例中:

#include <stdio.h>

int main(void)
{
    int a, b, c, d;

    a = b = c = d = 1;
    1 ? a++ : b ? c++ : d;

    printf("%d %d %d %d\n", a, b, c, d);
    return 0;
}

作业:

a = b = c = d = 1;

相当于:

a = (b = (c = (d = 1)));

相应地:

1 ? a++ : b ? c++ : d;

等同于:

1 ? a++ : (b ? c++ : d);

标准对最后一个案例怎么说?它是否保证从左到右计算这样的组合表达式(因此不计算 c++ 部分),就像赋值相反?

保证?:的求值顺序:先求第一个操作数,然后根据第一个操作数是否为真求值第二个或第三个操作数。

你主要是对运算符precedence/associativity求值顺序之间的关系感到困惑。他们扮演着不同的角色。前者决定运算符如何分组,而后者决定先评估哪个子表达式。

考虑表达式 a * b + c * d,优先规则意味着它等同于 (a * b) + (c * d)。但是是否保证编译器会在 c * d 之前评估 a * b?答案是否定的,在此示例中,运算符 + 不保证计算顺序。

条件运算符 ?: 是为数不多的具有指定计算顺序的运算符之一。 (其余为 &&||,)。


在你的例子中

1 ? a++ : b ? c++ : d;
1 ? a++ : (b ? c++ : d);

总是等价的,在两个表达式中,首先计算 1,因为它为真,所以接下来计算 a++,结束。

1 ? a++ : b ? c++ : d;

相当于

if (1) {
    a++;
}
else {
    if (b) {
        c++;
    }
    else {
        d;
    }
}

所以,输出将是

2 1 1 1

关联性优先级 未定义求值顺序。这些概念完全不相关。 C 中的求值顺序由 顺序 规则定义,而不是由优先级或关联性定义。

确实 a = b = c = d = 1; 关联 a = (b = (c = (d = 1)));,但这并不意味着应该首先评估 d = 1,尤其是在 C 语言中,赋值运算符的计算结果为右值。

关联性只是说 c 应该接收值 1 转换为 d 的类型("as if" 它是从 d 读取的)。但这并不意味着 d = 1 应该首先完成。在您的示例中,所有变量都具有相同的类型,这意味着整个事物完全等同于 a = 1; b = 1; c = 1; d = 1; 绝对任何顺序。 a = b = c = d = 1 表达式中没有排序。

相同的逻辑适用于 ?: 运算符。它的结合性只是告诉您哪个操作数属于哪个运算符。而且分组确实是1 ? a++ : (b ? c++ : d);。但是结合律并没有告诉你任何关于求值顺序的信息。 ?: 运算符中的评估顺序是单独且独立定义的:始终首先评估(排序)条件,然后评估一个(且仅一个)分支。在您的示例中,首先评估 1,然后评估 a++,其结果成为整个表达式的结果。 (b ? c++ : d) 部分甚至没有被触及。