标准C语言中,`(i) = 1` 是合法的吗?

8
我正在编写符合此标准的C语言编译器,如果我解析以下这样的语句:
int i;
(i) = 1;

我的编译器报告了一个错误,指出(i)是rvalue,不应该被赋值。

我检查了代码和规则,并发现以下内容: 在赋值表达式语义中:

赋值运算符的左操作数必须是可修改的lvalue。

赋值表达式具有分配后左操作数的值,但不是lvalue。

在我的情况下,有两个赋值表达式:(i) = 1和带括号的i。因此,(i)应该是rvalue。

所以我的问题是: 在这个C标准中,(i) = 1是否合法?


1
它仍然是一个lvalue,括号并不改变这一点。 - Jeff Mercado
1
大家好,请指出标准中哪个规则是合法的,我的编译器严格遵守这个规则。 - user2269707
括号中的 i 不是赋值表达式。赋值表达式并不意味着“参与赋值的表达式”或任何其他 (i) 可以符合的内容。赋值表达式就是赋值 - user2357112
@user2357112 i 是一个赋值表达式,AST 树是 EXPRESSION->ASSIGNMENT_EXPRESSION->CONDITIONAL_EXPRESSION->LOGICAL_OR_EXPRESSION->CAST_EXPRESSION->UNARY_EXPRESSION->POSTFIX_EXPRESSION->PRIMARY_EXPRESSION->IDENTIFIER。 - user2269707
@reavenisadesk:按照惯例,对于这种语法,标准将“赋值表达式”非终结符号称为“赋值表达式”,因为写成“赋值表达式或任何更高优先级的内容”会更加笨重,或者需要显式命名非终结符可以扩展到的每种可能类型的表达式。一个“赋值表达式”可以扩展到许多不同类型的表达式,但是赋值表达式特指中间带有赋值运算符并且左右有操作数的表达式。 - user2357112
显示剩余5条评论
3个回答

9

引用n1570(C11标准发布前的最后一份草案):

6.5.1 主表达式(强调是我的)

5 带括号的表达式是主表达式。它的类型和值与不带括号的表达式相同。如果不带括号的表达式分别是lvalue、函数设计者或void表达式,则它是一个lvalue、函数设计者或void表达式。

i是一个lvalue,因此根据上述规则,(i)也是一个lvalue。回答您的问题,表达式(i) = 1是有效的C语言。


1
@reavenisadesk - 除非你明确地看到黑白分明的rvalue,否则它不是一个rvalue。或者你是在说i = 1也是无效的吗? - StoryTeller - Unslander Monica
2
@reavenisadesk:语法并没有说明哪些是左值或右值,而是语义。对于赋值表达式,C 6.5.16 3(在“语义”标题下方)指出,赋值表达式(它是描述使用赋值运算符形成的表达式的短语,而不是赋值表达式,后者是语法中的一个标记)不是左值。它并没有说赋值表达式语法标记不是左值。 - Eric Postpischil
@StoryTeller“括号表达式不会从分配表达式继承值类别”,标准中的任何地方都可以证明这一点吗? - user2269707
1
@reavenisadesk - Eric解释得比我更优美。此外,我认为你在这里不公正地挑剔(i)。有关语义的同样观点适用于每种表达式。 - StoryTeller - Unslander Monica
@EricPostpischil,我只是有些不理解,您能否再解释一下? - user2269707
显示剩余12条评论

0

StoryTeller已经解释了在标准中为什么对于你的例子,表达式(i)仍然是一个lvalue,但我认为你没有必要因为规范而困扰,所以让我来尝试解决你的问题。

我检查了代码和规则,并发现在赋值表达式语义中:

赋值运算符的左操作数必须是可修改的lvalue。

赋值表达式具有赋值后左操作数的值,但不是lvalue。

整个引用都是指整个赋值表达式,而不是lhs或rhs。

“赋值运算符的左操作数必须是可修改的lvalue。”表示lhs必须是可修改的lvalue。

“赋值表达式具有赋值后左操作数的值,但不是lvalue。”表示整个赋值表达式本身作为结果具有lhs的值,并且本身是rvalue。

因此以下所有内容都是正确的:

int i;
  i <- modifiable lvalue

(i) = 1;
  (i) <- modifiable lvalue (per StoryTeller's answer)
  1 <- rvalue
  ((i) = 1) <- rvalue

这有什么重要性呢?考虑以下几点:

int i = 0, j = 0, k = 0;
i = j = k = 1;
// parsed as `i = (j = (k = 1))`
// the expression `k = 1` has the value `1` and is an rvalue
// the expression `j = (k = 1)` has the value `1` and is an rvalue

(i = 2) = 3;
// is invalid, the expression `i = 2` is an rvalue, but it may not be the lhs of the assignment

在我的情况下,有两个赋值表达式:(i) = 1 和括号中的i。所以(i)应该是一个右值。
不,那是不正确的。(i) = 1 是唯一的赋值表达式。有两个子表达式(一个带括号的标识符(i)和一个数值常量1)。

0

这个答案受到了 @Eric Postpischil 的启发。

assignment-expression 的生成如下:

<assignment-expression> ::= <conditional-expression>
                          | <unary-expression> <assignment-operator> <assignment-expression>

在标准中,“赋值表达式”具体指带有赋值运算符的表达式。因此:
<conditional-expression> is not an assignment expression
<unary-expression> <assignment-operator> <assignment-expression> is an assignment expresssion

因此规则如下:

赋值表达式在赋值后具有左操作数的值,但不是左值。

仅适用于生产<unary-expression> <assignment-operator> <assignment-expression>,而不适用于<conditional-expression>

在示例(i) =1中,i是一个<assignment-expression>而不是一个assignment expression,它是一个<conditional-expression>,因此它是一个左值,所以(i)是一个左值。


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