__cdecl調用 C語言變參

__cdecl調用

在C語言中,函數調用支持不定參數,例如printf函數,可以不知道參數的個數,eg:

void err_info(const char *fmt, ...)

{

va_list ap;

va_start(ap, fmt);

vfprintf(stderr, fmt, ap);

va_end(ap);

}

這是如何實現的呢?如何支持變參的呢?

下面簡要介紹下__cdecl調用方式,當然還有很多其他的調用方式,不過目前用不到

函數調用需要關注的幾個方面:

1、函數調用時參數是如何保存的?

2、是調用者來清理函數調用還是被調用者來清理函數調用?

3、函數調用時參數的順序是怎樣的?

4、可能會用到哪些寄存器?

先說第一個問題,這個是很容易的,大家都知道C語言中是用棧來保存函數調用時的參數的

下面再分析其他幾個問題:

在__cdcel標準中規定:

1、函數調用的參數入棧順序是從右到左,即先壓棧最右邊的參數,最後壓棧最左邊的參數

2、在函數調用結束後用調用者來清理棧

3、調用過程中一般會用到eax、ebx、ecx等寄存器

void func(int a, int b)

{

...

}

int main(void)

{

...

func(1,2);

...

}

下面以這個例子來說明:

在調用的過程中由於是從右到左入棧,所以壓棧的順序是先2後1,用匯編可以描述爲:

push 2

push 1

main函數對應的彙編大概是:

push 2

push 1

call func

add esp, 8 //清理棧,因爲在調用過程中esp指針向下(或者說是向低地址)移動了8個字節

ret

這是調用順序上

基於上面的幾點我們就可以理解C語言中是如何實現變參函數的調用了:

1、由於函數調用是從右到左入棧的,因此函數調用時最左邊的參數是最後一個入棧的,這樣就可以函數調用時最左邊的參數位置了

2、由於有format的指定,每個參數的大小是可知的,例如%d表示是int型,佔用4個字節等

3、根據上面的兩項就可以取出函數參數了。首先根據最左邊參數(此處的含義是最後一個確定參數,也就是該參數後面的參數都是可變參數)

(例如

void func(char *arg1, char *arg2, ...)

那麼最左邊的參數意思就是arg2,因爲它是最後一個確定參數

)

的位置可以找到函數調用時最左邊的參數,然後根據每個參數的大小,移動esp指針(一般是向高地址移動),就可以依次取出其他所有參數了

wiki上介紹:http://en.wikipedia.org/wiki/X86_calling_conventions

clip_image001

clip_image002

clip_image003

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章