编译器如何知道函数调用中的逗号不是逗号运算符?

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

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

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

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

看看C语言的语法。 该标准在附录A中完整列出。 它的工作方式是你可以遍历C程序中的每个标记,并将它们与语法中的下一个项目进行匹配。 在每一步你只有有限数量的选项,所以任何给定字符的解释将取决于它出现上下文 。 在语法中的每个规则内部,每一行都给出了程序匹配的有效select。

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

这是有效的,因为parameter-list规则小心使用assignment-expression规则,而不仅仅是纯expression规则。 expression可以包含逗号,而assignment-expression不能。 如果不是这种情况,那么语法就不明确,编译器在参数列表中遇到逗号时不知道该怎么做。

然而,例如, 不是函数定义/调用的一部分,或者ifwhilefor语句的左括号将被解释为expression式的一部分(因为没有其他选项,但是只有在开始的expression式在这一点是一个有效的select),然后,在括号内, expression语法规则将适用,并允许逗号运算符。

从C99 6.5.17:

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

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

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

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

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

如果一个逗号运算符被用作函数参数,就像这样使用它:

 sum((a,b)) 

当然这不会编译。

原因是C语法。 虽然其他人似乎都想举例说明,但真正的交易是标准(C99)中函数调用的短语结构语法。 是的,函数调用由应用于后缀expression式的()运算符组成(例如标识符):

  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 

逗号运算符只能出现在一个expression式中 ,即在语法中的更深处。 所以编译器将函数参数列表中的逗号作为分隔赋值expression式的逗号,而不是分隔expression式

现有答案说“因为C语言规范说这是一个列表分隔符,而不是一个运营商”。

然而,你的问题是问“编译器是怎么知道的……”,这完全不同:它和编译器知道printf("Hello, world\n");中的逗号真的没什么两样printf("Hello, world\n"); 不是逗号运算符:由于逗号出现的上下文,编译器“知道” – 基本上,之前已经发生了什么。

C语言可以用Backus-Naur Form (BNF)来描述 – 本质上,编译器的parsing器用于扫描input文件的一组规则。 C的BNF将区分这些语言中可能出现的不同逗号。

关于编译器如何工作以及如何编写 ,有很多很好的资源。

C99标准草案说 :

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

换句话说,“因为”。

这个问题有多个方面。 有一个看法是定义如此说。 那么,编译器如何知道这个逗号是在什么语境? 这是parsing器的工作。 对于C来说,语言可以被LR(1)parsing器( http://en.wikipedia.org/wiki/Canonical_LR_parser )parsing。

这种方式的工作原理是parsing器生成一堆构成parsing器可能状态的表。 在某些状态下只有一组符号是有效的,符号在不同的状态下可能有不同的含义。 parsing器知道它正在parsing一个函数,因为前面的符号。 因此,它知道可能的状态不包括逗号操作符。

我在这里很普通,但是你可以在Wiki中看到所有关于细节的信息。