三元条件和赋值运算符优先级

问题描述 投票:0回答:6

我对直接赋值和三元条件运算符的优先级感到困惑:

#include<stdio.h>
int main(void)
{
    int j, k;

    j = k = 0;
    (1 ? j : k) = 1; // first
    printf("%d %d\n", j, k);

    j = k = 0;
    1 ? j : k = 1; // second
    printf("%d %d\n", j, k);
    return 0;
}

我希望输出是:

1 0
1 0

但恰好是:

1 0
0 0

另外我收到这个警告:

main.cpp:20: 警告:语句无效

这是关于我评论为第二行的。

由于直接赋值运算符的优先级低于三元条件运算符,我希望注释为 first 和 second 的行是等效的。但可惜事实并非如此。

我用 g++ --version (Ubuntu 4.4.3-4ubuntu5) 4.4.3 试过这个

c++ operator-precedence conditional-operator
6个回答
21
投票

C/C++语言中的运算符优先级不是由表格或数字定义的,而是由语法定义的。这是 C++0x draft 章节 5.16 条件运算符 [expr.cond]:

的条件运算符语法
条件表达式:
    逻辑或表达式
    逻辑或表达式?表达式:赋值表达式

因此,当您在双冒号的左侧使用赋值时,像这个这样的优先级表是正确的,但在右侧使用时则不然。我不知道这种不对称的原因是什么。这可能是一个历史原因:在 C 中,条件结果不是左值,因此给它赋值是没有意义的,并且允许不带括号接受赋值在当时看起来很聪明。


14
投票

第二行相当于:

1 ? (j) : (k = 1);

等于:

j;

等于:

;

关键是三元条件运算符的两个操作数可以是表达式,所以运算符优先级在这里不相关。很简单,第二个操作数是赋值表达式

k = 1
.


9
投票
(1 ? j : k) = 1;

相当于,

if(true) j = 1;
else k = 1;

还有,

1 ? j : k = 1;

相当于,

if(true) j;  // warning: statement has no effect
else k = 1;

3
投票

在第二种情况下,

1 ? j : k = 1;

被评价为:

(1) ? (j) : (k = 1);

并且由于一个评估为

true
,表达式评估为
j
什么都不做。


0
投票

前面的答案解释了结果,但没有解开疑惑。我会尽力给出我的理解。如有错误,请指出。

编译器可能会分析并生成如下代码:

  1. 将明文代码解析为一棵树(代码明文 -> 语义单元)。

    原始代码分解为最小语义单元并格式化树。在这个过程中,编译器需要知道每个运算符的优先级。而根据C++运算符优先级,我们知道

    • ?:
      (条件运算符)与
      =
      (直接赋值运算符)具有相同的优先级,优先级= 16
    • 在这个优先级(==16)中,结合律是Right->Left,所以
      k = 1
      高于
      ?:
      .

    所以对于代码

    1 ? j : k = 1;
    ,树可能是这样的:

                   op(?:)
          /         |          \
         /          |           \
      [condition] [true-branch] [false-branch]
          1           j             op(=)
                                    /  \
                                   k    1
    
  2. 确定Eval顺序(生成中间表示)

    为了生成可运行的代码,编译器需要知道哪一部分应该先评估(运行)。根据求值顺序,我们知道对于算子

    ?:
    ,应该先求值
    [condition]
    ,再根据条件结果求值。它简单明了并遵循我们的直觉。

    所以想象一下最后运行的过程,

    1
    eval to true, true branch
    j
    then evaluated,
    k = 1
    never run.

所以知道

1,2
之后,我们就知道了充分的原因。

作为cpp文件,step

1
属于
compile-time
概念,而step
2
属于
runtime
概念。但是从编译器的角度来看,它们都在编译过程中,而
compile-time/runtime
概念只是从语言设计的角度来看。


0
投票

通过C++的优先级规则(https://en.cppreference.com/w/cpp/language/operator_precedence),

1 ? j : k = 1;

被评估从右到左,即

1 ? (j) : (k = 1);

[另见链接中的注释 2]。

这确实没有效果,因为只执行了中间表达式。

© www.soinside.com 2019 - 2024. All rights reserved.