先看一段代碼: mov eax,dword ptr [a] add eax,1 mov dword ptr [a],eax //這三行指令將a+1 mov ecx,dword ptr [a] mov dword ptr [ebp 0D0h],ecx //這兩行指令將a的值存儲到一個臨時地址(寄存器間 ...
先看一段代碼:
#include<iostream>
using namespace std;
void func(int a, int b)
{
cout << a << " " << b << endl;
}
int main()
{
int a = 0;
func(a++, ++a);
return 0;
}
由於C++是從右向左入棧(與編譯器的調用約定有關,不是C++標準的規定),所以剛看到代碼的時候,我以為結果會是“1 1”(先將++a入棧,再將a++入棧),然而事實是: 。這是在VS上面運行的,在g++上也是一樣的結果。幸好VS還可以查看彙編代碼。
由於在VS上有十分方便的反彙編工具,所以接下來說的東西都基於VS2019自帶的反彙編。
設置斷點並調試,便可以打開反彙編視窗:
下麵這段就是在調用func函數時的彙編代碼:
mov eax,dword ptr [a]
add eax,1
mov dword ptr [a],eax //這三行指令將a+1
mov ecx,dword ptr [a]
mov dword ptr [ebp-0D0h],ecx //這兩行指令將a的值存儲到一個臨時地址(寄存器間接定址)中
mov edx,dword ptr [a]
add edx,1
mov dword ptr [a],edx /這三行指令又將a+1
mov eax,dword ptr [a]
push eax //這兩行指令將a壓入棧
mov ecx,dword ptr [ebp-0D0h]
push ecx //這兩行指令將剛剛存儲的臨時地址的值壓入棧
call func (0A312E9h) //調用func函數
add esp,8 //esp為棧指針寄存器
如果將代碼改成
func(++a, ++a);
會是什麼結果呢?答案是
mov eax,dword ptr [a]
add eax,1
mov dword ptr [a],eax //這三行指令將a+1
mov ecx,dword ptr [a]
add ecx,1
mov dword ptr [a],ecx //這三行指令將a+1
mov edx,dword ptr [a]
push edx //這兩行指令將a壓入棧
mov eax,dword ptr [a]
push eax //這兩行指令將a壓入棧
call func (09912E9h)
add esp,8
可以看到,將a++改成++a之後,指令少了將a的值保存起來的步驟,並且由於並非在讀取參數時就壓入棧,而是在所有參數的表達式執行完之後才壓入,所以最後壓入參數棧的都是最後的變數a。
那麼結合彙編代碼理解文章開頭為什麼是“1 2”就不難了,基本就是這樣的區別(註意:讀取參數和執行參數的表達式時也是從右往左的):
在調用函數的時候,參數要避免使用自增和自減,避免出現混亂。
文章為作者結合個人理解編寫,可能會有錯漏的地方,如有錯誤,敬請指正。