va_start

va_start,函式名稱,讀取可變參數的過程其實就是在堆疊中,使用指針,遍歷堆疊段中的參數列表,從低地址到高地址一個一個地把參數內容讀出來的過程·

概述

由於在C語言中沒有函式重載,解決不定數目函式參數問題變得比較麻煩;即使採用C++,如果參數個數不能確定,也很難採用函式重載.對這種情況,有些人採用指針參數來解決問題.下面就c語言中處理不定參數數目的問題進行討論

在VC++6.0的include有一個stdarg.h頭檔案,有如下幾個宏定義:

#define _INTSIZEOF(n) ((sizeof(n)+sizeof(int)-1)&~(sizeof(int) - 1) )

#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) ) //第一個可選參數地址

#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) ) //下一個參數的值

#define va_end(ap) ( ap = (va_list)0 ) // 將指針置為無效

如果對以上幾個宏定義不理解,可以略過,接著看後面的內容.

在進程中,堆疊地址是從高到低分配的.當執行一個函式的時候,將參數列表入棧,壓入堆疊的高地址部分,然後入棧函式的返回地址,接著入棧函式的執行代碼,這個入棧過程,堆疊地址不斷遞減,一些黑客就是在堆疊中修改函式返回地址,執行自己的代碼來達到執行自己插入的代碼段的目的.

總之,函式在堆疊中的分布情況是:地址從高到低,依次是:函式參數列表,函式返回地址,函式執行代碼段.

堆疊中,各個函式的分布情況是倒序的.即最後一個參數在列表中地址最高部分,第一個參數在列表地址的最低部分.參數在堆疊中的分布情況如下:

最後一個參數

倒數第二個參數

...

第一個參數

函式返回地址

函式代碼段

代碼說明

void arg_test(int i, ...);

int main(int argc,char *argv[])

{

int int_size = _INTSIZEOF(int);

printf("int_size=%d\n", int_size);

arg_test(0, 4);

//arg_cnt(4,1,2,3,4);

return 0;

}

void arg_test(int i, ...)

{

int j=0;

va_list arg_ptr;

va_start(arg_ptr, i);

printf("&i = %p\n", &i);//列印參數i在堆疊中的地址

printf("arg_ptr = %p\n", arg_ptr);//列印va_start之後arg_ptr地址

/*這時arg_ptr應該比參數i的地址高sizeof(int)個位元組,即指向下一個參數的地址*/

j=*((int *)arg_ptr);

printf("%d %d\n", i, j);

j=va_arg(arg_ptr, int);

printf("arg_ptr = %p\n", arg_ptr);//列印va_arg後arg_ptr的地址

/*這時arg_ptr應該比參數i的地址高sizeof(int)個位元組,即指向下一個參數的地址,如果已經是最後一個參數,arg_ptr會為NULL*/

va_end(arg_ptr);

printf("%d %d\n", i, j);

}

說明:

int int_size = _INTSIZEOF(int);得到int類型所占位元組數

va_start(arg_ptr, i); 得到第一個可變參數地址

根據定義(va_list)&v得到起始參數的地址, 再加上_INTSIZEOF(v) ,就是其實參數下一個參數的地址,即第一個可變參數地址.

j=va_arg(arg_ptr, int); 得到第一個可變參數的值,並且arg_ptr指針上移一個_INTSIZEOF(int),即指向下一個可變參數的地址.

va_end(arg_ptr);置空arg_ptr,即arg_ptr=(void *)0;

擴展閱讀

相關領域:c語言 java BASIC Microsoft Visual C++ vc vhdl j2ee linux UML VF asp VB delphi JSP sql perl windows 彙編語言 C SHARP c語言程式設計 html。

相關詞條

相關搜尋

熱門詞條

聯絡我們