我正在观察我认为clang
和gcc
之间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。
答案 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));
}