关于printf输出结果的一些问题

小组通工的一个同学问了我一个问题,让我解释一下下面这个式子的结果为什么是那样的,前提赋值a = 1, b = 2, c = 3

上面是式子,下面结果

这个问题一开始我也想不太明白,我知道printf的运算规则是从右向左的,但是为什么第二个式子是3我就想不明白了,因为毕竟b == c的话不应该就只有0或1两种可能么。然后上网查了一下,看有人写了这么一个式子,赋值b = 0


观察了一下大概懂得了什么。

我认为,首先,printf在编译的时候,按传的参数的顺序从右向左的顺序编译一遍,这时候我们可以看到的是,第五个b,没有计算,继续第四个b ++此时运算的结果是0,然后b自加1变成1,第三个b同样没有计算,再看第二个++ b,运算的结果是2,b自加变成2,最后第一个b也没有计算,此时进行输出,所有对参数值变量b都按照b的最后结果输出,而那些计算式都按照计算的结果输出。

如果还是不太明白,那么我们再看一下一开始同学问我的问题:

第四个式子,这是一个典型的运算的式子,并没有赋值,经过计算,此时运算结果是0,然后a,b,c结果不变。第三个式子同样是一个计算式,计算的结果是0,然而因为赋值b = c所以b变成了3,第二个式子b == c判断结果完成后赋值给a,a = 1,然后再看第一个式子,c对b赋值,b对a赋值,a = c = 3,编译完成,输出结果,第一个第二个式子输出的都是a的值,所以都是3,然后第三个和第四个式子输出的都是运算的结果都是0。所以输出的结果是0.


这也就解释了为什么像我知道printf的运算法则但是还是不明白为什么输出结果是那个样子,我一开始尝试着把这四个参数单独拿出来printf,结果是3 1 0 0,也就是按照我的正常思维出来的结果,但是printf的输出法则告诉我们,对于变量的输出,输出的是其最后全部运算完成的结果,但是对于运算式的话,输出的结果是其运算所得到的结果。

最后说点关于printf的通用知识,那就是它会将参数压栈。这也就是为什么计算顺序按照从右向左,但是输出顺序从左向右

另外,这个是在GCC编译环境下实现的,VC下还是很有不同的,比如第一个例子会输出3 1 0 0,大概VC的库函数printf是压栈一个参数输出一个参数吧,暂时做这样的猜测。

错误更正:关于b++和++b的问题,一开始把他俩想成相同的自加的存在了,但是忽视了其运算的先后顺序的问题。还是拿个例子来说:


这个输出的结果就和我上面的推测结果不同,原因是++b输出的结果和b相同,都是b最后的结果。我们知道b++是先返回再自加,++b是先自加再返回,为了研究一下,准备看一下汇编代码,其实我们可以知道的是因为printf都是对结果进行压栈,所以每个式子最前面可以等同于加上一个无关变量,如a = ++b , c = b ++,然后压栈时将a和c压入。汇编代码如下:

这是设置的c = ++i

<span style="color:#ff0000;">012D1384  mov        eax,dword ptr [i] 
012D1387  add         eax,1 
012D138A  mov        dword ptr [i],eax 
012D138D  mov        ecx,dword ptr [i] 
012D1390  mov        dword ptr [c],ecx 
</span>

这里设置b = i ++

<span style="color:#ff0000;">012D1375  mov        eax,dword ptr [i] 
012D1378  mov        dword ptr [b],eax 
012D137B  mov        ecx,dword ptr [i] 
012D137E  add        ecx,1 
012D1381  mov        dword ptr [i],ecx </span>

我们可以看到第一个++i的过程是,先将i存入到寄存器eax中,然后对eax自加1,再把eax中的值给到i,再把i的值存入寄存器ecx中,最后将ecx中的值给到c过程完毕

i++的过程是,先存入eax中,然后立刻就将eax中的值给到b,然后后面三句就是i自加的过程。

通过汇编我们也就看懂了这个过程。我们可以看到,c是自加后返回的值,然后b是返回后自加,这就决定了++i最后的值是i最后的值,而i++的值是自加前返回的值。


如果什么纰漏,请指出,谢谢指导~

posted @ 2015-07-03 23:17  ChiLuManXi  阅读(395)  评论(0编辑  收藏  举报