我对直接赋值和三元条件运算符的优先级感到困惑:
#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/C++语言中的运算符优先级不是由表格或数字定义的,而是由语法定义的。这是 C++0x draft 章节 5.16 条件运算符 [expr.cond]:
的条件运算符语法条件表达式: 逻辑或表达式 逻辑或表达式?表达式:赋值表达式
因此,当您在双冒号的左侧使用赋值时,像这个这样的优先级表是正确的,但在右侧使用时则不然。我不知道这种不对称的原因是什么。这可能是一个历史原因:在 C 中,条件结果不是左值,因此给它赋值是没有意义的,并且允许不带括号接受赋值在当时看起来很聪明。
第二行相当于:
1 ? (j) : (k = 1);
等于:
j;
等于:
;
关键是三元条件运算符的两个操作数可以是表达式,所以运算符优先级在这里不相关。很简单,第二个操作数是赋值表达式
k = 1
.
(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;
在第二种情况下,
1 ? j : k = 1;
被评价为:
(1) ? (j) : (k = 1);
并且由于一个评估为
true
,表达式评估为j
什么都不做。
前面的答案解释了结果,但没有解开疑惑。我会尽力给出我的理解。如有错误,请指出。
编译器可能会分析并生成如下代码:
将明文代码解析为一棵树(代码明文 -> 语义单元)。
原始代码分解为最小语义单元并格式化树。在这个过程中,编译器需要知道每个运算符的优先级。而根据C++运算符优先级,我们知道
?:
(条件运算符)与=
(直接赋值运算符)具有相同的优先级,优先级= 16k = 1
高于?:
.所以对于代码
1 ? j : k = 1;
,树可能是这样的:
op(?:)
/ | \
/ | \
[condition] [true-branch] [false-branch]
1 j op(=)
/ \
k 1
确定Eval顺序(生成中间表示)
为了生成可运行的代码,编译器需要知道哪一部分应该先评估(运行)。根据求值顺序,我们知道对于算子
?:
,应该先求值[condition]
,再根据条件结果求值。它简单明了并遵循我们的直觉。
所以想象一下最后运行的过程,
1
eval to true, true branch j
then evaluated, k = 1
never run.
所以知道
1,2
之后,我们就知道了充分的原因。
作为cpp文件,step
属于1
概念,而stepcompile-time
属于2
概念。但是从编译器的角度来看,它们都在编译过程中,而runtime
概念只是从语言设计的角度来看。compile-time/runtime
通过C++的优先级规则(https://en.cppreference.com/w/cpp/language/operator_precedence),
1 ? j : k = 1;
被评估从右到左,即
1 ? (j) : (k = 1);
[另见链接中的注释 2]。
这确实没有效果,因为只执行了中间表达式。