1. 程式人生 > >C語言中調用匯編子過程時引數在棧中是如何呼叫的

C語言中調用匯編子過程時引數在棧中是如何呼叫的


在C語言中嵌入彙編子過程的新手大多都會有一個問題,在用匯編寫的子過程(函式)中到底以怎樣的形式來獲取傳進來的引數呢?這裡討論的是堆疊法來實現,下面是我個人的理解:(大神可以一笑而過)

針對這個問題,首先得明白呼叫一個子過程時棧的變化,因為在傳參時首先是將引數壓入棧中,而子函式想要呼叫就直接從棧中彈出即可。

下面是一個簡單的分析過程:

首先,在C中呼叫一個函式,擷取如下程式碼:

i = test(a, b);//在此我們不需要考慮test過程具體的執行內容。 

在執行此語句時,自然先執行:test(a, b); 相當於彙編的:call test
而call指令則會將test所用的引數逆序壓入棧中,在此處先壓入b,再壓入a。

call指令還有一個隱含的操作,即是將返回地址也跟著壓入棧中。

至此,call指令本身的操作就執行完畢了,接下來就轉交給呼叫的子過程test執行了。

那麼在test中是如何獲取傳進的引數呢?

在這裡已經很顯然了,此時棧指標%esp是指向的返回地址,那麼第一個引數的地址則是:%esp+4,第二個:%esp+8,如果有更多傳參話則依次類推。。。。

那麼,我們自己在用匯編寫的子過程中呼叫引數的時候就可以寫成

   mov r1, [ esp + 4 ]    //獲取引數a
   mov r2, [ esp + 8 ]   //獲取引數b
   ..........
   ret

記住了,這是我們自己寫的彙編語句,而如果我們的子過程乾脆就用c語言寫的話,c編譯器得到的彙編程式碼會不會也是這樣來獲取引數的呢?
答案是否定的。

c編譯器產生的彙編語句會有些區別,將程式碼複製如下:

test:
   push         ebp            //壓入舊的幀指標
   mov  ebp, esp            //獲取新的幀指標ebp
   mov r1, [ ebp + 8 ]     //獲取引數a
   mov r2, [ ebp + 12 ]   //獲取引數b
   ......
   ......
   pop        ebp
   ret

這裡多出來的幀指標是什麼呢?簡單來說,就是用來在棧中劃分當前過程的分界線,還有一個好處就是可以藉助其獲得引數,此處就是通過  ebp+8 ;ebp+12來獲取引數的。

寫到這裡,大家也能看到我們自己寫的彙編子過程與編譯器之間的不同了(我們當然也可以按照編譯器的習慣來寫彙編),最大的不同就是對於幀指標的用法,在c編譯器中會預設呼叫,則也導致最後要pop ebp;

而我們自己寫的雖然可以不用壓入幀指標,同時在最後也省去了pop ebp;但我們卻不能借助幀指標來獲取引數了,反正是各有利弊吧,這也與個人的習慣有關。

以上都是我自己的理解,想來應該與實際有些出入,有不對的地方還望指出。