【问题标题】:Assignment statement used in conditional operators条件运算符中使用的赋值语句
【发布时间】:2013-07-28 04:36:46
【问题描述】:

谁能告诉我为什么这个语句会出错 - Lvalue Required

(a>b?g=a:g=b); 

但这个是正确的

(a>b?g=a:(g=b));

其中a , bg 是整数变量,ab 作为键盘输入。

【问题讨论】:

  • 为什么不使用g = a > b ? a : b
  • @m0nhawk 好点。但可能是因为OP来自c++。
  • @m0nhawk:不会引出这么有趣的问题。这是一个很好的例子,可以很容易地避免通过阅读代码可能不会注意到的错误。我认为它甚至可以像 C++ 一样编译,因为 ?:= 在那里获得了相同的优先级。但这并不意味着您必须这样做,因为它可能会导致混乱和“意外”行为。

标签: c


【解决方案1】:

这个问题通常会引发一连串的答案,试图通过运算符优先级的概念来解释这种情况。实际上,它不能这样解释,因为这是输入的典型示例,“运算符优先级”之类的替代概念在此基础上分解。您可能知道,C 中确实没有“运算符优先级”。只有语法分组,通常无法通过运算符的任何线性排序来精确表达。

让我们来看看语言规范是怎么说的。在这种情况下,C 语法的相关部分是?: 运算符和= 运算符的语法。对于?: 运算符,它是

conditional-expression:
  logical-OR-expression
  logical-OR-expression ? expression : conditional-expression

对于= 运算符,它是

assignment-expression:
  conditional-expression
  unary-expression assignment-operator assignment-expression

在第一种情况下,关键部分是?: 运算符的最后一个操作数:它不是expression,而是conditional-expressionconditional-expression 是 C 表达式语法的不同入口点:它在不再可能将顶级 = 运算符包含到 conditional-expression 的位置“进入”语法。将= 运算符“走私”到conditional-expression 中的唯一方法是将语法一直下降到最底层

primary-expression:
  identifier
  constant
  string-literal
  ( expression )
  generic-selection

然后使用( expression ) 分支一直环绕到顶部。这意味着 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 根本无法解析为表达式(原因与上述相同)。

再次,正式地说,编译器“损坏”是不够的:编译器需要检测约束违规并发出一些诊断消息,这正是这里发生的情况。诊断消息的文本没有正确反映问题的性质这一事实是无关紧要的(编译器可以简单地说“哈哈哈!”)。但是,这种误导性诊断的一个不良后果是它会误导用户误解问题,顺便说一句,从发布到此问题的大量正式错误答案中可以明显看出这一点。

【讨论】:

    【解决方案2】:

    在表达式中,

    (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
    

    【讨论】:

    • 您解释的优先级更好,实际上是使用优先级规则编写的语法。 --高优先级运算符应稍后添加,以便出现在评估树中的叶节点。明白了吗??
    • @GrijeshChauhan;请改写您的句子应稍后添加高优先级运算符,以便出现在评估树中的叶节点。我不明白。
    • 阅读this answer 我解释了如何在语法中排列运算符,以便高优先级运算符首先计算——我认为这将非常有用。
    • @GrijeshChauhan;我在您提供的链接上阅读了该答案。这只是因为语法
    • 作为我和马里奥最近的讨论:我找到了precedence(?:) == precedence(=) 所以你添加了正确的最后一个链接。
    【解决方案3】:

    问题是operator precedence:在 C 中,三元条件运算符 (?:) 的优先级高于赋值运算符 (=)。

    如果没有括号(这里不做任何事情),您的表达式将是这样的:

    a > b ? g = a : g = b;
    

    其中具有最高优先级的运算符将是比较 >,因此您将在这里获得第一个逻辑分组:

    (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;
    }
    

    【讨论】:

    • 解释使用优先级对新手来说是更好的方法!
    • Mario 但问题是,如果您认为 C/C++ 在一起,这种解释会令人困惑,因为根据 precedence in C++precedence(?:) > precedence(=)(a>b?g=a:g=b); 是一个有效的表达式。
    • @JensGustedt 是的,但马里奥不是新人,所以我可以和他讨论,因为我也在向 hakssc 解释。是的,这两个答案都更好地解释了我的:)
    • @GrijeshChauhan C++ 中的优先级与 C 不同。再次检查表,两者都有15,这意味着它们是从右到左评估的。将该表与 C 表进行比较。该表达式可以在 C++ 中使用,但在 C 中不起作用。
    • @Mario 是的,你是对的!我错了...阅读此Order of Evaluation vs. Associativity 也不错]
    【解决方案4】:

    你的表达式(a>b?g=a:g=b)被解析为:

    (a>b?g=a:g)=b
    //        ^^^
    

    来自Microsoft documentation

     conditional-expression:
        logical-or-expression
        logical-or-expression ? expression : conditional-expression
    

    在 C 中,运算符 ?:precedence 比运算符 = 更高。那么这意味着( a ? b : c = d )将被解析为( a ? b : c ) = d。由于 l-value 的规则,第一个表达式也是有效的,但不是你想的那样。

    为了避免这个错误,你也可以这样做:

    g = ( a > b ) ? a : b;
    

    【讨论】:

    • 请解释原因,为什么解析为:(a>b?g=a:g)=b
    • @user2132128 再说一遍:C 语言的语法定义它是这样解析的。
    • 您的链接指向 C++ 文档,语法 sn-p 取自 C++ 语法。这个问题是关于C语言的。在这方面,C 语言语法与 C++ 语言语法有很大不同。无法解析 C 语法中的原始表达式。
    【解决方案5】:

    表达式:

    (a>b?g=a:g=b)
    

    解析为:

    (a>b?g=a:g)=b 
    

    我们不能给表达式赋值,所以它的左值错误。

    阅读:Conditional operator differences between C and 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);
    

    同样,您应该在;, 之后添加空格。

    【讨论】:

    • 你能解释一下原因吗
    • @user2132128 "lvalue" 可能会出现在左侧。这是可以分配的东西。比如你不能3 = x;
    • @MarounMaroun “左值”是可能出现在左侧的东西” - 正确。 - “这是可以分配的东西” - 错误。 这些是“可修改的左值”。const 对象和数组也是左值,但它们不能被分配。
    • @GrijeshChauhan 我是来帮忙的 :) (无论如何,是为了帮助那些应得的人......)嗯,并支持我为引用条件运算符的 CFG 所做的努力!
    • @GrijeshChauhan;涂层标准或语法是很好的做法,但对于新手来说不是一个好主意。
    【解决方案6】:
    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)
    

    【讨论】:

    • 我喜欢这个解决方案,它提供了一个更明智的选择。
    • 这并不能真正回答问题。
    猜你喜欢
    • 2013-07-15
    • 1970-01-01
    • 1970-01-01
    • 2017-03-27
    • 2014-05-04
    • 2018-06-09
    • 2011-03-11
    • 2012-03-26
    • 2012-04-19
    相关资源
    最近更新 更多