int a = 15;
printf("%d %d\n", ++a, a++);
老问题了,也没什么好说的,但是顺序点涉及到参数的情况还没仔细考虑过,所以分析下其内部原理。
我本来的考虑是按照 cdecl 压栈次序,a++ 先压入,为 15,此后++发生副作用,a 变为 16,然后压入 ++a,a 先自增,为 17,所以输出结果为 17 15。虽然心里忐忑了一下,但也就这样自欺欺人了一会,用 gcc 验证无误,然后 vc6 就给了我一棒子,它的输出为 16 15。
说实话参数里副作用的顺序点我没有研究过,所以导致了这个结果,于是好好看了下他们生产的汇编码:
VC6相应汇编代码:
5: int a = 15;
00401028 mov dword ptr [ebp-4],0Fh
6:
7: printf("%d %d\n", ++a, a++);
0040102F mov eax,dword ptr [ebp-4]
00401032 mov dword ptr [ebp-8],eax
00401035 mov ecx,dword ptr [ebp-8]
00401038 push ecx
00401039 mov edx,dword ptr [ebp-4]
0040103C add edx,1
0040103F mov dword ptr [ebp-4],edx
00401042 mov eax,dword ptr [ebp-4]
00401045 push eax
00401046 push offset string "%d %d\n" (00422020)
0040104B mov ecx,dword ptr [ebp-4]
0040104E add ecx,1
00401051 mov dword ptr [ebp-4],ecx
00401054 call printf (004010a0)
00401059 add esp,0Ch
可以看出,在 0x00401038 处,压入了第三个参数为 15,0x0040103C 处 a++ 的副作用发生,a 变为 16;0x00401045 处压入第二个参数为 16,直到 0x0040104E 处 ++a 的副作用才发生,输出结果为 16 15。我感觉这里的函数参数的逗号分隔符并不是逗号表达式,所以不是一个顺序点,导致 ++a 在这里的副作用不一定发生,所以此时 vc 没有增加 a 的值,是不是这样呢?难道对于函数参数,顺序点在函数结束处[待研究]?
再来看看 gcc 4.3 的情况,以下是同样程序它的汇编码:
0x00401146 <main+22>: movl $0xf,-0x8(%ebp)
0x0040114d <main+29>: mov -0x8(%ebp),%eax
0x00401150 <main+32>: addl $0x1,-0x8(%ebp)
0x00401154 <main+36>: addl $0x1,-0x8(%ebp)
0x00401158 <main+40>: mov %eax,0x8(%esp)
0x0040115c <main+44>: mov -0x8(%ebp),%eax
0x0040115f <main+47>: mov %eax,0x4(%esp)
0x00401163 <main+51>: movl $0x4020a0,(%esp)
0x0040116a <main+58>: call 0x4011d8 <printf>
可以看出,在 0x00401150 和 0x00401154 处 a 自增了 2 次,0x00401158 处压入 printf 的第三个参数为 15,0x0040115f 处压入第二个参数,而此时经过 0x0040115c,第二个参数已经为 17 了,所以结果为 17 15。
虽然是支末问题,不用纠缠,但是仍然引出了很多不确定的东西,当是学习了一次吧。