C/C++中作为函数参数的 i++ 与 ++i

平时写代码时,经常会遇到自加自减运算,我们知道这种自加自减运算有两种形式,一种就是我们所谓的前置++(或- -)(例如:++i),另一种是后置++(或- -)(例如: i++)。那么这两者的区别是什么呢?
为简单起见,我们以 i++ 和++i 为例,说明两者的区别: i++ 是先取 i 的值,再进行自加运算;而++i是先做自加运算,再取 i 的值。我们来看看下面的例子:

    int i = 5;
    int a = ++ i;

根据上面的分析,容易知道 a = 6,因为这里是前置++, 先做加1运算再取值。将上面代码的第二句换成 int a = i ++, 此时的 i 值就是5。这些理解起来都很简单,我们来分析下面的例子:

    int i = 0;
    printf ("%d, %d, %d\n",i ++, --i, i ++);

我们知道函数传参顺序是自右向左,那么最右边是i++, 根据前置++的特性,先取 i 的值为0,即压栈的参数值为0,再自加1得到 i = 1,;再看从右到左第二个参数,是前置- -,先做自减运算得到i = 0,取i 的值压栈,即此时压栈的也是0;最后看从右到左第三个参数,是后置++,先取 i 的值,即i = 0 进行压栈。这样分析下来我们打印出来的应该是:0, 0, 0。但是运行后我们却发现打印结果为: 0, 1, 0。为什么会出现这样的结果?这就要看在函数调用时,压栈参数的值到底是多少。下面我们从汇编的角度来分析这个结果,先来看printf那一行代码的汇编码:
printf (“%d, %d, %d\n”,i ++, –i, i ++);
我们分析从右向左第一个参数: i++
下面两个指令是将i的值0放在临时地址ebp-0D0h中(这里我们将其理解为临时量)
(即先保存值)
mov eax,dword ptr [i]
mov dword ptr [ebp-0D0h],eax

下面三条指令是从内存中取出i 的值,做+1运算后,将 i 的值 1 放入内存
(再进行自加运算)
mov ecx,dword ptr [i]
add ecx,1
mov dword ptr [i],ecx

第二个参数:- - i
下面三条指令是从内存中取出i的值1,进行自减运算得到i = 0,将0放入i的内存
(先进行自减运算)
mov edx,dword ptr [i]
sub edx,1
mov dword ptr [i],edx

第三个参数: i ++
下面两条指令是将 i 的值0放入临时地址ebp-0D4h中
(先保存i的值0)
mov eax,dword ptr [i]
mov dword ptr [ebp-0D4h],eax

下面三条指令是做自加运算,得到i的值1放入i的内存(注意此时i的内存中的值为1)
(再做自加运算)
mov ecx,dword ptr [i]
add ecx,1
mov dword ptr [i],ecx

这里才是重点,前面都是在计算或保存参数的值,我们来看看压栈的是什么值:
根据形参压栈顺序,第一个压栈的应该是最右边的i++,我们看到这里压栈的是临时量[ebp-0D0h]的值,即0,第二个压栈的是- -i,这里是从内存中取的值,即1,进行压栈,第三个压栈的是最左边的i ++,压栈的是临时量[ebp-0D4h]里面的值,即0。这样,我们就不难理解为什么打印结果是0, 1, 0,而不是我们所想的0, 0, 0.
mov edx,dword ptr [ebp-0D0h]
push edx
mov eax,dword ptr [i]
push eax
mov ecx,dword ptr [ebp-0D4h]
push ecx

从这个例子中,我们容易得到:当i ++ 和 ++i (当然也包括自减运算)作为函数实参时,编译器对两者取值的处理是不同的,是i++时,编译器是从临时量中取值的,而++i是从i的内存中取值的。所以,当参数列表中有多个前置++(或- -)时,他们最终打印的值是一样的,都是最终内存中存储的那个值。

猜你喜欢

转载自blog.csdn.net/trajectoryofbird/article/details/78330222