小组通工的一个同学问了我一个问题,让我解释一下下面这个式子的结果为什么是那样的,前提赋值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++的值是自加前返回的值。
如果什么纰漏,请指出,谢谢指导~