英文:
How are C declarations actually parsed, based on this interesting discrepancy?
问题
很常见看到声明和赋值结合在一起,像这样:
int beans = a * 2;
或者分开写,像这样:
int beans;
beans = a * 2;
我目前的理解是,beans可以被赋值,因为它是一个左值(lvalue),它有可以被写入的存储空间。而像 a * 2
这样的右值(rvalue)不能被赋值,因为它只是一个具有值的表达式,没有存储空间。因此,这是允许的:
int beans;
(beans) = a * 2;
实际上,任何赋值的左操作数都应该是一个左值。现在,这似乎表明 int beans;
是一个表达式,也是一个左值。然而,这 不被允许:
(int beans) = a * 2;
从C解析器的设计角度来看,这表明声明不仅仅是一个具有左值的表达式。这里发生了什么?
英文:
It's fairly common to see a declaration and assignment combined like this:
int beans = a * 2;
or separate, like this
int beans;
beans = a * 2;
My current understanding is that beans can be assigned to because it is an lvalue; it has storage that can be written to. An rvalue, like a * 2
cannot be assigned to since it's just an expression with a value, no storage. For this reason, this is allowed:
int beans;
(beans) = a * 2;
And in fact, any left operand to the assignment which is an lvalue should work. Now, this seems to suggest that int beans;
is an expression which is also an lvalue. However, this is not allowed:
(int beans) = a * 2;
In terms of how the C parser is designed, this suggests that declarations are something greater than just an expression with an lvalue. What is going on here?
答案1
得分: 6
以下是翻译好的部分:
这个语句
beans = a * 2;
包含许多表达式。
主要的表达式是赋值本身:beans = a * 2
。它又包含了两个子表达式:beans
和 a * 2
。乘法表达式本身又有子表达式:a
和 2
。
所有表达式都可以用括号括起来,这意味着整个语句可以改写成这样:
(beans) = ((a) * (2));
这里所有的子表达式都被括起来了。
现在我们来看看这个定义:
int beans;
那不是一个表达式。它不包含任何子表达式。不能把它整体括起来。
另一方面,带有初始化的定义:
int beans = a * 2;
包含一个表达式,带有子表达式。而且这在 =
的右边。所以我们可以写成:
int beans = ((a) * (2));
但是再次强调,变量声明部分不是一个表达式,不能被括起来。
另请注意,定义中的 =
不是 赋值。它是初始化。这两个不同的术语表示了语义上的区别。
正如Jonathan Leffler在评论中提到的,声明的声明符部分可以被括起来。
对于像
int beans;
这样的简单声明来说,使用它确实没有太多意义。但是对于指向函数或数组的指针之类的事情来说,这会有很大的区别。
示例:
int *foo(int x);
这声明了一个接受一个 int
参数并返回一个指向 int
的指针的函数。与下面的比较一下:
int (*foo)(int x);
这声明了一个变量,它是一个指向函数的指针。该函数接受一个 int
参数,并返回一个 int
值。
英文:
The statement
beans = a * 2;
contains many expressions.
The main expression is the assignment itself: beans = a * 2
. That in turn contains two sub-expressions: beans
and a * 2
. The multiplication expression have sub-expressions itself: a
and 2
.
All expressions can be parenthesized, which means the whole statement could look like this instead:
(beans) = ((a) * (2));
Here all the sub-expressions are parenthesized.
Now we come to the definition:
int beans;
That is not an expression. It doesn't contain any sub-expressions. It can not be parenthesized as a whole.
The definition with initialization, on the other hand:
int beans = a * 2;
do contain an expression, with sub-expressions. And that is on the right side of the =
. So we can write it as:
int beans = ((a) * (2));
But again, the variable declaration part is not an expression, and can't be parenthesized.
Also please note that =
in a definition is not assignment. It's initialization. There's a semantic difference indicated by the two different terms.
As mentioned in a comment by Jonathan Leffler. the declarator part of a declaration can be parenthesized.
For a simple declaration like
int beans;
it really makes no sense to use it. But for things like pointers to functions or pointers to arrays it makes a large difference.
Example:
int *foo(int x);
That declares a function that takes one int
arguments and returns a pointer to int
. Compare to the following:
int (*foo)(int x);
That declares a variable, which is a pointer to a function. The function takes one int
argument, and returns an int
value.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论