其实很简单 一个关于运算符的问题 为什么在不同的编译器下会得到不同的结果 题目是
int i=3,k=0;
k=(++i)+(++i)+(++i);
System.out.println(k);
在C中是
printf("%d\n",k);
同样的题目为什么会有不一样的结果,答案是
在java中的 15
在linux 下用gcc编译是16
而用turbo C 得到的结果却是 18
这些编译器上存在些什么区别呢?
其实很简单 一个关于运算符的问题 为什么在不同的编译器下会得到不同的结果 题目是
int i=3,k=0;
k=(++i)+(++i)+(++i);
System.out.println(k);
在C中是
printf("%d\n",k);
同样的题目为什么会有不一样的结果,答案是
在java中的 15
在linux 下用gcc编译是16
而用turbo C 得到的结果却是 18
这些编译器上存在些什么区别呢?
跟语言规范中的规定相关。如果语言规范对某些行为没有严格定义,那么编译器的实现就可以自行发挥。
Java的语言规范明确规定了[url=http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#15.7]表达式的求值顺序是从左向右(15.7小节)[/url]。楼主的代码里,每个++i都是双目运算符+的操作数根据从左向右的求值顺序,要求第一个+的值,就必须先求第一个++i的值,结果是3+1;然后求第一个+右边的操作数,是“(++i) + (++i)”,于是重复求值过程,第二个++i就是4 + 1,第三个++i就是5 + 1,全部加起来就是15。由于语言规范做了严格的规定,所有符合规范的Java编译器都应该编译出同样效果的程序。
C语言则没有规定表达式的求值顺序,也没有严格规定“一个完整表达式里副作用的顺序”。[url=http://www.open-std.org/jtc1/sc22/wg14/www/projects#9899]C99语言规范[/url]写明++E完全等价于(E+=1)(6.5.3.1小节)。赋值表达式的规定在6.5.16,说赋值表达式的副作用应该发生在前一个和后一个顺序点之间,并且求值顺序未定义。关于顺序点的规定在附录C,楼主代码中,
[code="c"]int i = 3, k = 0;[/code]
这句有两个顺序点,分别是i = 3后的逗号和k = 0后的分号处。
[code="c"]k = (++i) + (++i) + (++i);[/code]
这整个是一个“表达式语句”,只有一个顺序点,就在末尾的分号处。
根据C99的规定,++i的副作用发生在k=0;之后和(++i);之间的任意位置都可以。所以不同的编译器就采取了不同的实现方式,GCC对该表达式的理解是:
[code="c"]i += 1;
i += 1;
temp = i + i;
i += 1;
temp += i;
k = temp;[/code]
Turbo C和VC9的理解是:
[code="c"]i += 1;
i += 1;
i += 1;
temp = i + i;
temp += 1;
k = temp;[/code]
而这都是C语言规范所允许的。之所以定义得比较宽松,是为了让编译器实现者能更自由的选用优化方式来产生高效的代码。
无论如何,良好的代码风格中,一个完整的表达式里最好不要有多余一个副作用,不然代码就会难以阅读。在C、C++等没有定义求值顺序的语言中,依赖求值顺序去求表达式的值更是很危险的作法,因为编译器的不同版本间都有可能改变求值顺序,更不提不同编译器间的不同实现。不过了解一下为什么会出现差异还是有好处的……
遇到问题先看规范,没错的。