在C语言中使用三目运算符出现错误

6

我有一段C语言代码如下:

main()
{
    int a=10, b;
    a>=5 ? b=100 : b=200 ;
    printf("%d" , b);
}

在Unix中使用GCC编译器运行代码会生成编译时错误,错误信息为“需要左值作为赋值运算符的左操作数”,并将错误指向b = 200,而在Windows上使用Turbo C编译则输出200。请问有人可以解释一下这种情况到底是什么意思吗?

6
使用括号来明确所需的运算符优先级。(不要使用 Turbo C。它已经过去20多年了!) - Code-Apprentice
1
http://stackoverflow.com/questions/12068118/use-of-brackets-in-expression-that-include-ternary-operator - Laura Maftei
5个回答

16

在C语言中,三目运算符的定义如下:

逻辑或表达式 ? 表达式 : 条件表达式

这里的条件表达式定义如下:

In C the ternary operator is defined like

logical-OR-expression ? expression : conditional-expression

where conditional expression is defined like

logical-OR-expression

赋值运算符的优先级低于OR运算符。因此,您必须编写

a >= 5 ? b = 100 : ( b = 200 );

否则编译器会将表达式视为

( a >= 5 ? b = 100 :  b ) = 200;

由于C语言中三元运算符的结果不是左值,所以上述表达式无效,编译器会发出错误。

来自C标准:

结果是第二个或第三个操作数(两者中计算的那个)的,转换为下面描述的类型

和脚注:

110) 条件表达式不产生左值。

请注意,在C和C++中,这个运算符的定义有本质区别。在C++中它被定义为

logical-or-expression ? expression : assignment-expression

在C++中同样的代码可以被GCC成功编译。

#include <iostream>

int main() 
{
    int a = 10, b;

    a >= 5 ? b = 100 : b = 200;

    std::cout << "b = " << b << std::endl;

    return 0;
}

这看起来不正确。在C中,赋值语句的形式是“一元表达式 op 赋值表达式”。因此,“否则编译器会将表达式视为…”是不正确的。编译器只会将该表达式视为语法上的无意义。任何合理的错误信息,如“需要左值(lvalue)”只是质量保证(QoI)。 - Johannes Schaub - litb
如果您运行此语句,则编译器将发出错误,如“需要左值作为赋值的左操作数”。因此,我的答案没有问题。当编译器遇到赋值运算符时,它期望左操作数是左值。因此,它会像这样解析语句:(a> = 5?b = 100:b)= 200; - Vlad from Moscow
你写道编译器将表达式视为“(...) = 200”,我理解为“根据C语言,该表达式应被视为(...) = 200”,这就是答案错误的原因。因为它甚至不是一个表达式。结尾的“= 200”无法解析。 - Johannes Schaub - litb
期望并不指导解析。客观的语法和语法规则才是关键。 - Johannes Schaub - litb
@JohannesSchaub-litb编译器识别条件运算符并从最左边的标记开始分析该行。我在先前的评论中显示的错误消息是关于语言语义的,而不是语言语法的。当编译器遇到赋值运算符时,它会发出错误消息,指出已识别的左操作数不是lvalue。所以我的答案没有问题。顺便说一句,您可以在不写错误评论的情况下对正确答案进行投票。:) - Vlad from Moscow
显示剩余4条评论

2
你可以把它放在大括号里使其生效..就像这样
(a>=5)?(b=100):(b=200);

请为您的函数main()指定一个返回类型


1
这个错误是由条件表达式的语法引起的。
logical-OR-expression ? expression : conditional-expression

因此,冒号后面的部分必须能够解析b = 200。然而,条件表达式无法解析它,因为赋值表达式的优先级较低 - 您需要在赋值表达式周围加上括号。
a>=5 ? b=100 : (b=200);

但是需要在这里加括号并不意味着表达式会被解析为 (a>=5 ? b=100 : b) = 200,这只是编译器内部的产物,在错误消息中提到赋值左操作数。C语言对于赋值表达式语法有以下两个规则,匹配的规则会被应用。
conditional_expression
unary_expression '=' assignment_expression

这会干扰递归下降解析器,它们只会调用parseConditionalExpression,并检查其后的标记。因此,一些C解析器实现选择不在此处给出语法错误,而是将其解析为语法上方的conditional_expression '=' ...,稍后在检查解析树时验证左侧是否为lvalue。例如,Clang源代码如下所示。
/// Note: we diverge from the C99 grammar when parsing the assignment-expression
/// production.  C99 specifies that the LHS of an assignment operator should be
/// parsed as a unary-expression, but consistency dictates that it be a
/// conditional-expession.  In practice, the important thing here is that the
/// LHS of an assignment has to be an l-value, which productions between
/// unary-expression and conditional-expression don't produce.  Because we want
/// consistency, we parse the LHS as a conditional-expression, then check for
/// l-value-ness in semantic analysis stages.

"GCC解析器的源代码说:"
/* ...
In GNU C we accept any conditional expression on the LHS and
diagnose the invalid lvalue rather than producing a syntax
error. */

0

在这种情况下,括号应该放在条件周围。

(a>=5) ? b=100 : b=200; 应该正确编译

RE: K&R The C Programming Language (2nd):

条件表达式的第一个表达式周围不需要括号,因为?:的优先级非常低,仅高于赋值。(强调是我的)然而,建议还是加上括号,因为它们使得条件部分更容易看到。


-7

试试这个!因为三元运算符返回值,所以你必须将其分配给b

#include <stdio.h>

main() {

    int a = 10, b;
    b = a >= 5 ? 100 : 200;
    printf("%d" , b);

}

谢谢,但我的问题是关于同一代码在两个不同编译器中响应不同的情况。您能否详细说明这一点?再次感谢 :) - user3778845
3
不是未定义的。 - Oliver Charlesworth

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接