[英]Why results of GCC and Clang are different with following code?
对于以下使用gcc和clang的代码,我得到了不同的结果,我相信这不是一个严重的错误,但是我想知道哪个结果与标准更一致? 非常感谢您的回复。
我使用gcc(Ubuntu 7.3.0-27ubuntu1〜18.04)7.3.0和clang版本6.0.0-1ubuntu2(tags / RELEASE_600 / final)
#include <stdio.h>
int get_1(){
printf("get_1\n");
return 1;
}
int get_2(){
printf("get_2\n");
return 2;
}
int get_3(){
printf("get_3\n");
return 3;
}
int get_4(){
printf("get_4\n");
return 4;
}
int main(int argc, char *argv[])
{
printf("%d\n",get_1() + get_2() - (get_3(), get_4()));
return 0;
}
gcc的结果是
get_3
get_1
get_2
get_4
-1
of的结果是
get_1
get_2
get_3
get_4
-1
C在评估某些运算符的操作数时不施加顺序。 在C标准中,评估的顺序由顺序点决定 。 当存在序列点时,该语言的良好实现必须先评估序列点左侧的所有内容,然后才能开始评估右侧的内容。 +
和-
运算符不包含任何序列点。 这是5.1.2.3 p2的定义
在执行序列中某些特定的点(称为顺序点)上,以前评估的所有副作用都应完整,并且以后评估的副作用都不应发生。
在你的表情
get_1() + get_2() - (get_3(), get_4())
你有+
, -
和逗号,
运营商。 只有逗号强加了一个计算顺序,而+
和-
则没有。
的,
之间get_3()
和get_4()
是在唯一的序列点printf("%d\\n",get_1() + get_2() - (get_3(), get_4()));
只要get_3()
在get_4()
之前发生,就可以按照编译器定义的任何顺序进行get_x
调用。
您正在看到未指定行为的结果。
有两个不同但相关的术语在起作用: 运算符优先级和求值顺序 。
运算符优先级指示解析顺序:
()
。 没什么奇怪的,它们是后缀,属于它们的运算符,即函数名。 接下来,我们有二进制+
和-
运算符。 它们属于同一运算符组“加法运算符”,并且具有相同的优先级。 发生这种情况时,该组操作员的操作员关联性决定了应按哪个顺序解析他们。
对于加法运算符,运算符的关联性是从左到右。 这意味着该表达式可以保证被解析为(get_1() + get_2()) - ...
一旦按以上方法算出了运算符优先级,我们便知道哪些运算数属于哪些运算符。 但这并没有说明表达式将按什么顺序执行。 这就是评估顺序的来源。
通常,C用干燥的标准术语表示:
除非后面有说明,否则子表达式的副作用和值计算是不排序的。
用简单的英语来说,这意味着在大多数情况下,操作数的评估顺序是不确定的,但有一些特殊的例外。
对于加法运算符+
和-
,这是正确的。 给定a + b
我们不知道a
或b
将首先执行。 评估的顺序是不确定的-编译器可以按自己喜欢的任何顺序执行它,不需要记录如何做,甚至不必视情况而定。
C标准有意将此保留为未指定,以允许不同的编译器以不同的方式解析表达式。 本质上是允许他们将其表达式树算法保留为编译器商业秘密,以允许某些编译器在自由市场上比其他编译器产生更有效的代码。
这就是为什么gcc和clang给出不同结果的原因。 您已经编写了依赖于评估顺序的代码。 这都不是任何编译器的错-我们不应该简单地编写依赖于行为不当的程序。 如果必须按一定顺序执行这些功能,则应将它们分成几行/多个表达式。
对于逗号运算符,这是罕见的特殊情况之一。 它带有一个内置的“序列点”,该序列点确保始终在对左操作数进行求值(执行)之前将其赋值。 其他此类特殊情况是&& ||
运算符和?:
运算符。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.