为什么gcc不将逗号视为序列点?

时间:2015-12-27 01:37:39

标签: c gcc clang

我正在观察我认为clanggcc之间C标准的一部分的不同行为(我的mac或linux上的自制软件版本)。问题是参数列表中的逗号是否是序列点。 clang将其解释为此类,但gcc不会。

此代码演示了此问题:

#include <stdio.h>

int inner(int *v) {
  *v += 1;
  return 1;
}

int outer(int x, int y) {
  return y;
}

int main(int argc, char *argv[]) {
  int x = 4;
  printf ("result=%d\n", outer(inner(&x), x));
}

结果:

$ clang -o comseq comseq.c && ./comseq
result=5
$ gcc-4.8 -o comseq comseq.c && ./comseq
result=4
$ gcc-5 -o comseq comseq.c && ./comseq
result=4

目前我无权访问C标准的副本,但在我看到clang的解释之前,我非常确定gcc的行为是正确的。使用--std=选项并没有改变我的结果。

ETA

在更好的阅读中,这个问题在https://stackoverflow.com/a/4176333/3171657中得到了回答:

  

a,b(§5.18)(在func(a,a ++)中)不是逗号运算符,它只是参数a和a ++之间的分隔符。在这种情况下,如果a被认为是a,则行为未定义原始类型)

但是这虽然非常有用,但是答案很长,所以留下这个问题可以帮助像我这样的其他用户。另外,要迂腐,那个问题是关于C ++,而不是C。

2 个答案:

答案 0 :(得分:5)

参数列表中的逗号不是序列点 - 它们不是术语含义内的逗号运算符。

关于函数调用的标准部分(ISO / IEC 9899:2011)(第6.5.2.2节)说:

  

¶3后缀表达式后跟括号(),后面包含一个可能为空的逗号分隔表达式列表,是一个函数调用。后缀表达式表示被调用的函数。表达式列表指定函数的参数。

     

¶4参数可以是任何完整对象类型的表达式。在准备对函数的调用时,将对参数进行求值,并为每个参数分配相应参数的值。

     

...

     

¶10在评估函数指示符和实际参数之后但在实际调用之前有一个序列点。调用函数(包括其他函数调用)中的每个评估(在执行被调用函数体之前或之后没有特别排序)对被调用函数的执行进行不确定的排序。

(我省略了几个脚注,但内容与讨论没有密切关系。)

没有任何关于在任何特定序列中评估的表达式。

另请注意,逗号运算符会产生单个值。如果您解释:

function(x, y)

作为逗号运算符,该函数只有一个参数 - y的值。当然,这不是功能的工作方式。如果你想要一个逗号运算符,你必须使用额外的括号:

function((x, y))

现在使用单个值调用function(),其值为y,但在评估x之后进行评估。这种符号很少被使用,尤其是因为它可能会让人感到困惑。

答案 1 :(得分:4)

函数参数的评估顺序未指定,因此用作参数分隔符的逗号不是序列点。

N1256 6.5.2.2函数调用

  

10函数指示符的评估顺序,实际   参数和实际参数中的子表达式是   未指定,但在实际调用之前有一个序列点。

如果在参数中使用逗号运算符,它将是一个序列点。

#include <stdio.h>

int inner(int *v) {
  *v += 1;
  return 1;
}

int main(int argc, char *argv[]) {
  int x = 4;
  printf ("result=%d\n", (inner(&x), x));
}