谁能告诉我为什么这个语句会出错 - 需要 Lvalue
(a>b?g=a:g=b);
但这一个是正确的
(a>b?g=a:(g=b));
其中a , b
和g
是整数变量, 和a
被b
视为键盘输入。
谁能告诉我为什么这个语句会出错 - 需要 Lvalue
(a>b?g=a:g=b);
但这一个是正确的
(a>b?g=a:(g=b));
其中a , b
和g
是整数变量, 和a
被b
视为键盘输入。
在表达式中,
(a > b ? g = a : g = b);
关系运算符>
具有最高优先级,因此a > b
被分组为操作数。条件表达式运算符? :
具有次高优先级。它的第一个操作数是a>b
,第二个操作数是g = a
。但是,条件表达式运算符的最后一个操作数被认为是g
而不是g = b
,因为这种情况g
与条件表达式运算符的绑定比它与赋值运算符的绑定更紧密。= b
因为没有左操作数(左值),所以出现语法错误。
您应该使用括号来防止此类错误并生成在您的第二个语句中完成的更具可读性的代码
(a > b ? g = a : (g = b));
其中最后一个操作数g = b
具有: ?
左值g
,这就是它正确的原因。
或者你可以做
g = a > b ? a : b
表达方式:
(a>b?g=a:g=b)
解析为:
(a>b?g=a:g)=b
而且我们不能分配给一个表达式,所以它的左值错误。
阅读:C 和 C++ 之间的条件运算符差异Charles Bailey 的回答:
语法?:
如下:
conditional-expression:
logical-OR-expression
logical-OR-expression ? expression : conditional-expression
这意味着a ? b : c = d
解析为(a ? b : c) = d
即使(由于“不是左值”规则)这不会导致有效的表达式。
一方面说明:
例如,请在你的表达中保留空间,以便它变得可读。
(a>b?g=a:g=b);
应该写成:
(a > b? g = a: g = b);
;
同样,您应该在和之后添加空格,
。
问题是运算符优先级:在 C 中,三元条件运算符 ( ?:
) 的优先级高于赋值运算符 ( =
)。
没有括号(这里不做任何事情),你的表达将是这样的:
a > b ? g = a : g = b;
其中具有最高优先级的运算符将是 compare >
,因此您将在这里获得第一个逻辑分组:
(a > b) ? g = a : g = b;
下一个最高的表达式是三元条件,它产生以下表达式:
((a > b) ? (g = a) : (g)) = b;
如您所见,您现在将在赋值运算符的左侧得到一个左值(即一个值;而不是一个变量),这是行不通的。
正如您已经注意到的,解决此问题的方法是简单地对您自己的表达式进行分组。我什至会考虑这种好的做法,特别是如果你不确定你的优先级会如何发挥作用。如果您不想考虑,请添加括号。只要记住代码的可读性,如果可以的话,自己解决运算符优先级,以确保一切正确且可读。
至于可读性:我可能会if()
在这里使用经典或将赋值运算符移到三元条件之外,这就是您通常定义的方式max()
:
g = a > b ? a : b;
或者更一般地作为宏或内联函数:
#define max(a, b) ((a) > (b) ? (a) : (b))
inline int max(int a, int b) {
return a > b ? a : b;
}
您的表达式(a>b?g=a:g=b)
被解析为:
(a>b?g=a:g)=b
// ^^^
从微软文档:
conditional-expression:
logical-or-expression
logical-or-expression ? expression : conditional-expression
在 C 中,运算符的优先级?:
高于运算符。那么这意味着将被解析为. 由于 l-value 的规则,第一个表达式也是有效的,但不是你想的那样。=
( a ? b : c = d )
( a ? b : c ) = d
为避免此错误,您还可以执行以下操作:
g = ( a > b ) ? a : b;
if(a>b)
{
g = a;
}
else
{
g = b;
}
可以用这个代替
g = a > b ? a : b; //if a>b use the first (a) else use the second (b)
这个问题通常会引发一连串的答案,试图通过运算符优先级的概念来解释情况。实际上,它不能这样解释,因为这是输入的典型示例,“运算符优先级”之类的替代概念在此基础上分解。您可能知道,C 中实际上没有“运算符优先级”。只有语法分组,通常无法通过运算符的任何线性排序来精确表达。
让我们看看语言规范是怎么说的。在这种情况下,C 语法的相关部分是?:
operator 和=
operator 的语法。对于?:
运营商来说
conditional-expression:
logical-OR-expression
logical-OR-expression ? expression : conditional-expression
对于=
运营商来说
assignment-expression:
conditional-expression
unary-expression assignment-operator assignment-expression
在第一种情况下,关键部分是?:
operator 的最后一个操作数:它不是 a expression
,而是 a conditional-expression
。是 C 表达式语法的conditional-expression
一个不同入口点:它在不再可能将顶级=
运算符包含到 a中的位置“进入”语法conditional-expression
。=
将运算符“走私”到 a 中的唯一方法conditional-expression
是将语法一直下降到最底层
primary-expression:
identifier
constant
string-literal
( expression )
generic-selection
( expression )
然后使用分支一直环绕到顶部。这意味着 a只有在显式包装在 中时conditional-expression
才能包含运算符。例如,语法禁止您作为运算符的最后一个操作数。如果你想要这样的东西,你必须明确地用括号括起来:.=
(...)
g = b
?:
<smth> ? <smth> : (g = b)
第二条语法存在非常相似的情况:赋值运算符。赋值的左侧(LHS)是unary-expression
。并且unary-expression
在包含顶级?:
运算符为时已晚的地方“进入” C 表达式的一般语法。到达?:
操作员的唯一方法unary-expression
是一直下降到primary-expression
分支( expression )
处。这意味着语法禁止您a > b ? g = a : g
作为运算符的 LHS 操作数=
。如果你想要这样的东西,你必须明确地将它作为父级:(a > b ? g = a : g) = <smth>
.
出于这个原因,声称“运算符优先级”的“流行”答案使语言将您的表达式解析为
(a > b ? g = a : g) = b
实际上是完全不正确的。实际上,正式的 C 语法中没有派生树可以使您的输入符合 C 语言的语法。您的输入根本无法解析。它不是一种表达方式。它只是在语法上无效。C 语言将其视为句法乱码。
现在,在实践中,您可能会看到一些实现响应“需要左值作为赋值的左操作数”诊断消息。形式上,这是一个误导性的诊断信息。由于上述输入不满足 C 语言表达式的语法,因此其中没有“赋值”,没有“左操作数”,也没有有意义的“左值”要求。
为什么编译器会发出这个奇怪的信息?他们很可能确实将此输入解析为有效的 C 表达式
(a > b ? g = a : g) = b
的结果?:
永远不是 C 中的左值,因此是错误的。但是,对您的输入的这种解释是非标准(扩展?)行为,在正式的 C 语言中没有基础。特定实现的这种行为可能是由于它们试图协调 C 和 C++ 语法(这在这方面完全不同)、它们试图产生更具可读性(尽管是“假”)的错误消息或其他原因造成的。
通常,在这样的实现中,类似的问题也会在输入的情况下弹出,例如
a + b = 5
将发出相同的错误,提示进行(a + b) = 5
解析,而从迂腐的角度来看a + b = 5
,根本不能将其解析为表达式(出于与上述相同的原因)。
同样,正式地说,这还不足以说编译器“损坏”:编译器需要检测约束违规并发出一些诊断消息,这正是这里发生的情况。诊断消息的文本没有正确反映问题的性质这一事实是无关紧要的(编译器可以简单地说“哈哈哈!”)。但是,这种误导性诊断的一个不良后果是它会误导用户误解问题,顺便说一句,从发布到该问题的大量正式错误答案中可以明显看出这一点。