根据这个有趣的差异,C 声明实际上是如何解析的?

Aar*_*ell 2 c parsing initialization assignment-operator language-lawyer

像这样组合声明和赋值是很常见的:

int beans = a * 2;
Run Code Online (Sandbox Code Playgroud)

或分开,像这样

int beans;
beans = a * 2;
Run Code Online (Sandbox Code Playgroud)

我目前的理解是bean可以被赋值,因为它是左值;它具有可写入的存储空间。右值a * 2无法分配给它,因为它只是一个带有值的表达式,没有存储空间。因此,这是允许的:

int beans;
(beans) = a * 2;
Run Code Online (Sandbox Code Playgroud)

事实上,任何作为左值的左操作数都应该有效。现在,这似乎表明int beans;是一个表达式,也是一个左值。但是,是不允许的:

(int beans) = a * 2;
Run Code Online (Sandbox Code Playgroud)

就 C 解析器的设计方式而言,这表明声明不仅仅是带有左值的表达式。这里发生了什么?

Som*_*ude 6

该声明

beans = a * 2;
Run Code Online (Sandbox Code Playgroud)

包含很多表达方式。

主要表达式是赋值本身:beans = a * 2。它又包含两个子表达式:beansa * 2。乘法表达式本身有子表达式:a2

所有表达式都可以加括号,这意味着整个语句可能如下所示:

(beans) = ((a) * (2));
Run Code Online (Sandbox Code Playgroud)

这里所有的子表达式都被括号括起来。

现在我们来看看定义:

int beans;
Run Code Online (Sandbox Code Playgroud)

那不是一个表达。它不包含任何子表达式。它不能作为一个整体括起来。

另一方面,带有初始化的定义:

int beans = a * 2;
Run Code Online (Sandbox Code Playgroud)

确实包含一个表达式和子表达式。那是在右侧=。所以我们可以将其写为:

int beans = ((a) * (2));
Run Code Online (Sandbox Code Playgroud)

但同样,变量声明部分不是表达式,并且不能用括号括起来。


另请注意,=定义中不是赋值。这是初始化。这两个不同的术语表明存在语义差异。


正如乔纳森·莱夫勒 (Jonathan Leffler) 在评论中提到的。声明的声明部分可以用括号括起来。

对于像这样的简单声明

int beans;
Run Code Online (Sandbox Code Playgroud)

使用它确实没有任何意义。但对于诸如函数指针或数组指针之类的东西,它会产生很大的差异。

例子:

int *foo(int x);
Run Code Online (Sandbox Code Playgroud)

它声明了一个函数,该函数接受一个int参数并返回一个指向 的指针int。与以下内容进行比较:

int (*foo)(int x);
Run Code Online (Sandbox Code Playgroud)

它声明了一个变量,它是一个指向函数的指针。该函数接受一个int参数,并返回一个int值。

  • 请注意,您可以在“声明符”中包含括号:`int (beans);` 或 `int (beans) = 3;` — 甚至 `int ((((beans)))) = { 3 };` (您可以在标量的初始化器周围包含大括号)。标准的相关部分是[§6.7.6 声明符](http://port70.net/~nsz/c/c11/n1570.html#6.7.6)。 (4认同)