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

时间:2013-06-29 18:35:17

标签: c function compiler-construction comma-operator

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

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

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

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

6 个答案:

答案 0 :(得分:49)

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

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

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

但是,例如,不是功能定义/调用的一部分,或ifwhilefor的左括号语句,将被解释为表达式的一部分(因为没有其他选项,但只有当表达式的开头是该点的有效选择时),然后,在括号内,expression语法规则将apply,允许逗号运算符。

答案 1 :(得分: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))

当然,这不会编译。

答案 2 :(得分: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

逗号运算符只能出现在表达式中,即在语法中进一步向下。因此编译器将函数参数列表中的逗号视为分隔赋值表达式的逗号,而不是将表达式分开。

答案 3 :(得分:11)

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

但是,你的问题是“编译器如何知道......”,这完全不同:它与编译器知道printf("Hello, world\n");中的逗号不是逗号运算符的方式完全没有区别:由于逗号出现的上下文,编译器“知道” - 基本上,之前已经发生了什么。

C'语言'可以在Backus-Naur Form(BNF)中描述 - 实质上是编译器parser用来扫描输入文件的一组规则。 BNF for C将区分这些不同语言中可能出现的逗号。

有很多关于编译器如何工作的好资源,how to write one

答案 4 :(得分:6)

The draft C99 standard says

  

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

换句话说,“因为”。

答案 5 :(得分:1)

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

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

我在这里非常一般,但你可以阅读Wiki中的所有细节。