38

考虑函数调用(调用int sum(int, int)

printf("%d", sum(a,b));

编译器如何确定,函数调用sum(int, int)中使用的不是逗号运算符?

注意:我不想在函数调用中实际使用逗号运算符。我只是想知道编译器如何知道它不是逗号运算符。

4

6 回答 6

49

查看 C 语言的语法。它在标准的附录 A 中完整列出。它的工作方式是,您可以逐步遍历 C 程序中的每个标记,并将它们与语法中的下一项匹配。在每个步骤中,您只有有限数量的选项,因此任何给定字符的解释将取决于它出现的上下文。在语法中的每条规则中,每一行都为程序提供了一个有效的替代方案来匹配。

具体来说,如果您查找parameter-list,您会看到它包含一个显式逗号。因此,只要编译器的 C 解析器处于“参数列表”模式,它找到的逗号将被理解为参数分隔符,而不是逗号运算符。括号也是如此(也可以出现在表达式中)。

这是有效的,因为该parameter-list规则谨慎使用assignment-expression规则,而不仅仅是普通expression规则。Anexpression可以包含逗号,而 anassignment-expression不能。如果不是这种情况,则语法将是模棱两可的,并且编译器在遇到参数列表中的逗号时将不知道该怎么做。

但是,例如,属于函数定义/调用或if,whilefor语句的左括号将被解释为表达式的一部分(因为没有其他选项,但前提是表达式的开头在这一点上是一个有效的选择),然后,在括号内,expression将应用语法规则,并且允许逗号运算符。

于 2013-06-29T19:06:25.270 回答
26

从 C99 6.5.17 开始:

如语法所示,逗号运算符(如本小节所述)不能出现在使用逗号分隔列表中的项目(例如函数的参数或初始化程序列表)的上下文中。另一方面,在这种情况下,它可以用在带括号的表达式中或条件运算符的第二个表达式中。在函数调用中

f(a, (t=3, t+2), c)

该函数有三个参数,第二个的值为 5。

另一个类似的例子是数组或结构的初始化列表:

int array[5] = {1, 2};
struct Foo bar = {1, 2};

如果要将逗号运算符用作函数参数,请像这样使用它:

sum((a,b))

当然,这不会编译。

于 2013-06-29T18:37:32.457 回答
19

原因是 C 语法。虽然其他人似乎都喜欢引用这个例子,但真正的问题是标准 (C99) 中函数调用的短语结构语法。是的,函数调用由()应用于后缀表达式的运算符组成(例如标识符):

 6.5.2 postfix-expression:
       ...
       postfix-expression ( argument-expression-list_opt )

和...一起

argument-expression-list:
       assignment-expression
       argument-expression-list , assignment-expression    <-- arglist comma

expression:
       assignment-expression
       expression , assignment-expression                  <-- comma operator

逗号运算符只能出现在表达式中,即在语法中更靠后的地方。因此,编译器将函数参数列表中的逗号视为一个分隔assignment-expressions,而不是一个分隔表达式

于 2013-06-29T19:12:31.963 回答
11

现有答案说“因为 C 语言规范说它是列表分隔符,而不是运算符”。

但是,您的问题是问“编译器如何知道...”,这完全不同:这与编译器如何知道逗号printf("Hello, world\n");不是逗号运算符实际上没有什么不同:编译器“知道”是因为逗号出现的上下文 - 基本上,之前发生了什么。

C“语言”可以用Backus-Naur 形式(BNF) 来描述 - 本质上,是编译器的解析器用来扫描您的输入文件的一组规则。C 的 BNF 将区分语言中这些不同可能出现的逗号。

关于编译器如何工作以及如何编写一个.

于 2013-06-29T20:13:47.957 回答
6

C99 标准草案说

如语法所示,逗号运算符(如本小节所述)不能出现在使用逗号分隔列表中的项目(例如函数的参数或初始化程序列表)的上下文中。另一方面,在这种情况下,它可以用在带括号的表达式中或条件运算符的第二个表达式中。在函数调用f(a, (t=3, t+2), c)中,函数有三个参数,第二个参数的值为 5。

换句话说,“因为”。

于 2013-06-29T18:40:27.813 回答
1

这个问题有多个方面。一个标准是定义是这样说的。那么,编译器如何知道这个逗号在什么上下文中呢?这就是解析器的工作。特别是对于 C,语言可以由 LR(1) 解析器 ( http://en.wikipedia.org/wiki/Canonical_LR_parser ) 解析。

它的工作方式是解析器生成一堆表,这些表构成了解析器的可能状态。只有特定的一组符号在某些状态下是有效的,而这些符号在不同的状态下可能有不同的含义。由于前面的符号,解析器知道它正在解析一个函数。因此,它知道可能的状态不包括逗号运算符。

我在这里很笼统,但是您可以在 Wiki 中阅读有关详细信息的所有信息。

于 2013-06-29T21:39:01.410 回答