棧在計(jì)算機(jī)領(lǐng)域中是個(gè)經(jīng)常提到的名詞,數(shù)據(jù)結(jié)構(gòu)中有棧;網(wǎng)絡(luò)傳輸中有協(xié)議棧。今天我們討論的調(diào)用棧(call stack),指的是在程序的執(zhí)行過(guò)程中存儲(chǔ)函數(shù)調(diào)用信息的動(dòng)態(tài)數(shù)據(jù)結(jié)構(gòu)。
這個(gè)定義可能太抽象了一些,在給出具體的例子之前,請(qǐng)大家先思考一個(gè)問(wèn)題,哪些信息是函數(shù)調(diào)用過(guò)程中所需要的?或者這么問(wèn),一個(gè)編譯器,在面對(duì)一個(gè)函數(shù)的調(diào)用指令時(shí),該生成哪些代碼?
首先,函數(shù)的返回地址要保存下來(lái)。就好像你和你的小狗玩仍飛碟游戲,每一個(gè)函數(shù)調(diào)用好比扔一個(gè)飛碟,當(dāng)你的狗狗哼茲哼茲的撿來(lái)飛碟,函數(shù)完執(zhí)行的時(shí)候,它一定得知道去哪里把飛碟還給你。
然后,函數(shù)的參數(shù)是個(gè)必不可少的元素,這個(gè)很直觀,就不多羅嗦了。第三,被調(diào)用的函數(shù)的局部變量也要存儲(chǔ)在棧上。因?yàn)楦鶕?jù)局部標(biāo)量的定義,對(duì)相同函數(shù)的不同調(diào)用,局部變量有不同的存儲(chǔ)空間,不會(huì)互相影響,所以這些數(shù)據(jù)也是跟函數(shù)調(diào)用息息相關(guān)的信息。
下面,我們通過(guò)一個(gè)例子,來(lái)看看函數(shù)的調(diào)用棧中的信息:
對(duì)于下面一段c++程序
#include
int SumFromOne(int d)
{
int sum = 0xabcd;
if (d == 1)
sum = 1;
else
sum = d + SumFromOne(d-1);
return sum;
}
void main()
{
int sum = SumFromOne(10);
printf("sum=%d", sum);
}
編譯之,Cl /Zi a.cpp (/Zi生成pdb,調(diào)試的時(shí)候使用)
大家選用熟悉的調(diào)試器,在這里,筆者用的是windbg 大家可以去這個(gè)地址下載(http://www.microsoft.com/whdc/devtools/debugging/installx86.Mspx)
從調(diào)試器中啟動(dòng)程序:Windbg a.exe
然后在第4行設(shè)置一個(gè)斷點(diǎn)(F9)。開始執(zhí)行這個(gè)程序(F5),直到程序中斷在斷點(diǎn)處找到程序的調(diào)用棧:
1.察看當(dāng)前的ebp,在command窗口中應(yīng)該已經(jīng)看到。否則的話,在command中輸入r
2.在memory察看窗口中,virtual欄中輸入ebp-10的值,并且把display format改成long hex,以利于觀察棧中的值。
我把我的windbg截圖粘貼如下,并和大家一起觀察幾個(gè)地方

1.返回地址0040106b。參見反匯編的結(jié)果,0040106b正是main調(diào)完SumFromOne之后的那條指令。
2.參數(shù)。主程序傳給他的是10,(0xa),在memory窗口ebp+8的位置找到他。
3.局部變量,我在程序中故意將sum初始化為0xabcd,大家可以在memory窗口ebp-4的位置找到他。
有興趣地同學(xué)可以按F5,在下一個(gè)斷點(diǎn)中察看相關(guān)信息。
好,今天的這片博客中我們對(duì)調(diào)用棧有了感性的認(rèn)識(shí),在ebp周圍找到了返回地址,參數(shù)以及局部變量。下一片博客中,我將解釋為什么這些信息存儲(chǔ)在這些位置,敬請(qǐng)期待。
這個(gè)定義可能太抽象了一些,在給出具體的例子之前,請(qǐng)大家先思考一個(gè)問(wèn)題,哪些信息是函數(shù)調(diào)用過(guò)程中所需要的?或者這么問(wèn),一個(gè)編譯器,在面對(duì)一個(gè)函數(shù)的調(diào)用指令時(shí),該生成哪些代碼?
首先,函數(shù)的返回地址要保存下來(lái)。就好像你和你的小狗玩仍飛碟游戲,每一個(gè)函數(shù)調(diào)用好比扔一個(gè)飛碟,當(dāng)你的狗狗哼茲哼茲的撿來(lái)飛碟,函數(shù)完執(zhí)行的時(shí)候,它一定得知道去哪里把飛碟還給你。
然后,函數(shù)的參數(shù)是個(gè)必不可少的元素,這個(gè)很直觀,就不多羅嗦了。第三,被調(diào)用的函數(shù)的局部變量也要存儲(chǔ)在棧上。因?yàn)楦鶕?jù)局部標(biāo)量的定義,對(duì)相同函數(shù)的不同調(diào)用,局部變量有不同的存儲(chǔ)空間,不會(huì)互相影響,所以這些數(shù)據(jù)也是跟函數(shù)調(diào)用息息相關(guān)的信息。
下面,我們通過(guò)一個(gè)例子,來(lái)看看函數(shù)的調(diào)用棧中的信息:
對(duì)于下面一段c++程序
#include
int SumFromOne(int d)
{
int sum = 0xabcd;
if (d == 1)
sum = 1;
else
sum = d + SumFromOne(d-1);
return sum;
}
void main()
{
int sum = SumFromOne(10);
printf("sum=%d", sum);
}
編譯之,Cl /Zi a.cpp (/Zi生成pdb,調(diào)試的時(shí)候使用)
大家選用熟悉的調(diào)試器,在這里,筆者用的是windbg 大家可以去這個(gè)地址下載(http://www.microsoft.com/whdc/devtools/debugging/installx86.Mspx)
從調(diào)試器中啟動(dòng)程序:Windbg a.exe
然后在第4行設(shè)置一個(gè)斷點(diǎn)(F9)。開始執(zhí)行這個(gè)程序(F5),直到程序中斷在斷點(diǎn)處找到程序的調(diào)用棧:
1.察看當(dāng)前的ebp,在command窗口中應(yīng)該已經(jīng)看到。否則的話,在command中輸入r
2.在memory察看窗口中,virtual欄中輸入ebp-10的值,并且把display format改成long hex,以利于觀察棧中的值。
我把我的windbg截圖粘貼如下,并和大家一起觀察幾個(gè)地方

1.返回地址0040106b。參見反匯編的結(jié)果,0040106b正是main調(diào)完SumFromOne之后的那條指令。
2.參數(shù)。主程序傳給他的是10,(0xa),在memory窗口ebp+8的位置找到他。
3.局部變量,我在程序中故意將sum初始化為0xabcd,大家可以在memory窗口ebp-4的位置找到他。
有興趣地同學(xué)可以按F5,在下一個(gè)斷點(diǎn)中察看相關(guān)信息。
好,今天的這片博客中我們對(duì)調(diào)用棧有了感性的認(rèn)識(shí),在ebp周圍找到了返回地址,參數(shù)以及局部變量。下一片博客中,我將解釋為什么這些信息存儲(chǔ)在這些位置,敬請(qǐng)期待。

