同一段代码,不同C编译器,输出结果真的会不同啊!!!

来源:互联网 发布:中国科技大学知乎 编辑:程序博客网 时间:2024/04/29 02:20

一个奇怪的问题啊。

写了这么久代码,总认为,只要是C编译器,同一段代码总会有相同运行结果的(不考虑随机以及多线程的情况)。

但是,今天在百度知道上看到了一段代码,运行结果不同,本来打算直接说——小子,机器出问题了吧!!但是,抱着治学严谨的态度,我对本段代码进行了测试,发现结果出乎意料。。

先看这段代码吧。

#include<stdio.h>void fun();int main(){fun();return 0;}void fun(){int i = 3;    printf("%d,%d,%d,%d,%d,%d,%d\n",i++,++i,i--,--i,i--,i--,i+5);printf("%d\n",i);printf("%d\n\n",i);}
应该会有一个比较确定的运行结果的,但是,事实是这样的。

VisualStudio2013RC运行结果如下:(应该所有VC运行结果都一样吧,毕竟,都是同一个模子里出来的)


Code Blocks运行结果如下:


C-Free 5.0(一个比较小的C编译器,经常用来测试代码)运行结果如下:


现象已经描述出来了,那么,原因是什么呢?窃以为,作为C编译器,计算规则应该都是相同的,所以应该不会出现上述情况得啊。百度知道上的那哥们是在两个不同的系统下测试,我这个全部在Window操作系统下测试,应该与系统无关,所以,问题肯定处在编译器上,具体原因正在研究中。以上现象警示我们,对于一段代码,千万别一口咬定了会出现什么结果,只有实践才是检验真理的最好工具啊!!!同时,欢迎筒子们对上述现象进行各种猜测式的解释。

为了解释上述现象,我进行了下面的测试。

以下是几种情况的测试

测试一:

#include<stdio.h>void fun();int main(){fun();return 0;}void fun(){int i = 3;printf("%d,%d\n",i++,i++);printf("%d\n",i);}
3个编译器的结果均为:

测试二:

#include<stdio.h>void fun();int main(){fun();return 0;}void fun(){int i = 3;printf("%d,%d\n",++i,++i);printf("%d\n",i);}

本例就出现分歧了:

CodeBlocks与VS结果为:


而,C-Free结果为:


测试三:

#include<stdio.h>void fun();int main(){fun();return 0;}void fun(){int i = 3;printf("%d,%d\n",i++,++i);printf("%d\n",i);}
测试结果为:

同样,CodeBlocks与VS“沆瀣一气”,而C-Free不同,因此,可以得出结论,C-Free前置加加的计算规则与CodeBlocks和VS不同(顿时后背发凉,我用它测试了很多代码的,那么,会有多少潜在错误让我给漏了呢!!!)。

上图:


上面是兄弟二人的运行结果,下面是C-Free的


测试四:

#include<stdio.h>void fun();int main(){fun();return 0;}void fun(){int i = 3;printf("%d,%d\n",++i,i++);printf("%d\n",i);}
大家猜猜这次运行结果是什么样的呢?

CodeBlocks还和VS一样滴么?。。。

上图:(兄弟3个居然是一样滴。。。)



从以上4个测试实例,应该能够得到结论如下:

1、从测试一知道:在上面的3中编译器中,当++后置时,计算顺序从右往左,但是,总会有一步延迟。

2、从测试二知道:在VS与CodeBlocks中,当++前置时,在变量用到前,已经执行结束,并且,在同一个printf中的++均会在该变量的所有操作前就执行完毕。而在C-Free中,仍然会从右往左计算。

3、从测试三知道:在VS与CodeBlocks中,计算顺序依旧是从右向左,这样,对于printf("%d,%d\n",i++,++i),先计算了++i,后计算了i++,所以,第一个输出为4。(因为后置++有延迟,所以,当输出第一个i时,i++并没有进行运算)。其次,对于前置++来说,当在变量使用前,必须是已经将所有++运算处理完后的结果,所以,在输出++i的结果时,会输出5,此时i++已经操作完成。为了说明这一点,我特地设置了测试五:

测试五:

#include<stdio.h>void fun();int main(){fun();return 0;}void fun(){int i = 3;printf("%d,%d,%d\n",i++,i++,++i);printf("%d\n",i);}
结果:

VS与CodeBlocks为:


C-Free为:

从测试五可以看出,VS与CodeBlocks中,printf("%d,%d,%d\n",i++,i++,++i)均是先计算的++i,然后依次是第二个(从左往右数)i++,第一个i++。但是,在输出++i之前,所有计算都已经完成,所以,++i的输出结果是6.

4、从测试四知道:VS与CodeBlocks中,printf("%d,%d\n",++i,i++)也是从右往左进行计算的,即,先计算了i++,然后计算的++i,但是,在输出i++时,因为++操作是后置的,所以,并没有自加,因此,它的输出为3。而输出++i时,++i与i++均已经运行完成,所以,输出结果为5。

从上面的5个测试中,我们已经基本明白了自加操作的执行顺序,现在还差一个问题没有解决,那就是当printf中自增自减操作与加常数操作并存时,他们的操作顺序是如何的呢?让我们看看测试六。

测试六:

#include<stdio.h>void fun();int main(){fun();return 0;}void fun(){int i = 3;printf("%d,%d,%d,%d\n",i++,i++,++i,i+5);printf("%d\n",i);}
本次测试结果差异较大:

VS结果:


CodeBlocks:


C-Free:


从结果可以看出:当有常量操作时(即存在i+常数),VS的处理方式是,在进行常量操作时,i的所有自增操作全部执行结束,即其可以看成前置++操作,所以,在上面的测试中,VS的结果会是11;而CodeBlocks则不同,它将常量操作与自增操作看成是一种操作,均从右往左进行运算,因此,i+5第一个运算,此时i并没有进行任何自增操作,所以,结果为8。

总结:(因为C-Free受众面较小,并且并不知道其权威性,故我就不就其进行讨论了,这里仅仅讨论CodeBlocks与VS的情况)

1、printf()操作分两步完成:

第一步:参数入栈:

在入栈时,各种变量运算进行执行。

第二步:参数出栈:

在出栈时,输出栈中的结果,如果栈中压入的是变量,则输出变量本身的值,如果压入的计算公式,则需要重新计算(对VS的"i+常量"而言),而如果压入的是数值,则将该数值输出。

2、printf()压栈规则:

后入栈,也就是参数从右往左入栈。

3、前置加加与后置加加的区别:

前置操作压栈时,压入的是变量;后置操作压栈时,压入的是常量(即运算结果)。

前置操作在压栈时,已经进行了前置运算。也就是说,对于++i,压栈时,i已经完成了自加,并且,压入栈的是i本身,而不是i的值。

后置操作在压栈后,相关变量才完成后置操作运算。也就是说,对于i++,压栈时,i并没有完成自加,并且,压入栈的是i的值,而非i变量本身。

4、VS与CodeBlock中,i+常量操作的处理:

在VS中“i+常量”操作在压栈时,压入的是“i+常量”运算,此处的i是变量。

在CodeBlocks中,“i+常量”操作在压栈时,压入的是“i+常量”的运算结果,压入的是数值。

注意:以上观点全由实验获得,并没有官方文档支持。所以,如果有什么错误之处,欢迎批评指正。

至此,对于各个编译器的基本操作规则都已经基本了解,那么,让我们再看看初始实例吧。

初始实例中的操作:

printf("%d,%d,%d,%d,%d,%d,%d\n",i++,++i,i--,--i,i--,i--,i+5);

首先,printf参数入栈,各种自加操作进行运算,计算顺序为从右往左,

i+5, i--, i--, --i, i--, ++i, i++

每次压栈时,i的值为:

3, 3, 2, 0, 0, 0, 0

3 i+5,本操作并不会改变i的值,但是在压栈时,VS会压入"i+5",而CodeBlocks则会压入"i + 5"的值,即8

3 i--,本操作为后置操作,所以,首先会将i的值压栈,即压入3,然后对i进行自减操作,即,i的实际值现在为2

2 i--,在上一步操作后,i的值为2,因为本操作为后置操作,所以,将2压栈后,i再进行自减操作。此时i的值为1

0 --i,在上一步操作后,i的值为1,因为本操作位前置操作,所以,在压栈时进行了自减操作,此时i的值为0,并且,将变量i压栈(并非i的值)

0 i--,在上一步操作后,i的值为0,因为本操作位后置操作,所以,将0压栈后,i再进行自减操作。此时i的值为-1

0 ++i,在上一步操作后,i的值为-1,因为本操作位前置操作,所以,在压栈时进行了自加操作,此时i的值为0,并且,将变量i压栈(并非i的值)

0 i++,在上一步操作后,i的值为0,因为本操作位后置操作,所以,将0压栈后,i再进行了自加操作。此时i的值为1

然后,printf参数出栈,输出输出结果。输出顺序是从左往右的。

那么,在输出时,每一步应该输出的值为:

0 压入栈的是值,所以直接输出

1 因为压入栈的是变量i,而此时变量i的值为1,故输出1

0 压入栈的是值,所以直接输出了压栈时候的值

1   因为压入栈的是变量i,而此时变量i的值为1,故输出1

2 因为压入栈的是值,所以直接输出

最后一个值是VS与CodeBlocks的不同之处。

6 因为在VS中,常量操作压入的是"i + 5"操作,而此时i的值为1,故输出的计算结果为6.

8 在CodeBlocks中,常量操作压入的是"i + 5"的值,故输出的是压栈时的运算结果8.




原创粉丝点击