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

hac*_*cks 37 c compiler-construction function comma-operator

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

printf("%d", sum(a,b));
Run Code Online (Sandbox Code Playgroud)

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

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

ams*_*ams 49

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

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

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

但是,左括号,例如,是不是一个函数的定义/调用的一部分,或者if,while或者for声明,将被解释为一个表达式的一部分(因为没有其他选择,但只有一个表达式的开始在那一点上是一个有效的选择),然后,在括号内,expression语法规则将适用,并允许逗号运算符.

  • 我忘记了这个名字有一个技术术语.我只是意味着任何给定的令牌只能在它出现的上下文中被理解.换句话说,我使用"上下文敏感"作为形容词而不是名词.但是,我怀疑只有被这个混淆的人才是已经知道答案的人! (4认同)

Yu *_*Hao 26

从C99 6.5.17:

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

f(a, (t=3, t+2), c)
Run Code Online (Sandbox Code Playgroud)

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

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

int array[5] = {1, 2};
struct Foo bar = {1, 2};
Run Code Online (Sandbox Code Playgroud)

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

sum((a,b))
Run Code Online (Sandbox Code Playgroud)

当然,这不会编译.

  • 是的但不是问题的答案. (5认同)

Jen*_*ens 19

原因是C语法.虽然其他人似乎都想引用这个例子,但实际的交易是标准(C99)中函数调用的短语结构语法.是的,函数调用包含()应用于后缀表达式的运算符(例如标识符):

 6.5.2 postfix-expression:
       ...
       postfix-expression ( argument-expression-list_opt )
Run Code Online (Sandbox Code Playgroud)

和...一起

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

expression:
       assignment-expression
       expression , assignment-expression                  <-- comma operator
Run Code Online (Sandbox Code Playgroud)

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

  • 为了扩展@Jens的答案:让我们改变问题并简化它.而不是"表达"让我们有高尔夫球(涂成黄色),还有大的透明塑料球,可以打开并且里面塞有东西:`(`stuff`)`.实际上,语法说你可能有黄色的高尔夫球,它们是自动分开的.或者,只要你使用了两半*,你就可以提供一个清晰的球*.透明球作为一个单元工作,它不能打开和分离.所以:f((a,b),g)有一个"晴球"(a,b)和一个"黄色球"g,因此恰好有两个球,呃,参数. (4认同)
  • 我没有评论室,所以,继续,然后回到真正的C语法:括号允许你逃脱到一个"完整的"表达式,其中逗号是逗号表达部分.但是,在你有一个"额外"的开括号之前,你正处于这个更有限的"赋值表达式"子语法(就像"黄色高尔夫球"的想法),其中不允许使用逗号.如果解析器在此上下文中遇到逗号,则必须停止并完成赋值表达式.这是有效的,因为`(`"完成"与`)`:包围结束了完整的表达式上下文. (2认同)
  • 嗯,我没有任何其他自然语言来表达这一点.考虑`{`...`}`,`[`...`]`和`(```)`.他们"匹配":如果你写'a [3}`,那显然是错的.如果你写'a [(3)`它仍然显然是错误的.`(`只能通过匹配的`来结束`).这会"关闭"整个序列,清楚地说明了什么是什么. (2认同)

Rod*_*ddy 11

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

但是,你的问题是"编译器如何知道...",这完全不同:它与编译器知道逗号printf("Hello, world\n");不是逗号运算符的方式完全没有区别:编译器'知道'因为逗号出现的上下文 - 基本上,以前的内容.

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

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


unw*_*ind 6

C99标准草案说:

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

换句话说,"因为".

  • 我的孩子们不接受这个答案为什么OP应该......但这就是原因,因为这个含糊不清的案例是被禁止的. (6认同)