在#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) ) //第一個可選參數地址htm
#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) ) //下一個參數地址進程
#define va_end(ap) ( ap = (va_list)0 )get
函數實現的原理:test
首先:進程中的堆棧地址是從高到底分配的,那麼執行一個函數的過程:原理
先把參數地址入棧,接着是函數返回值地址入棧,接着入棧函數的執行代碼。這個過程當中地址不斷遞減(一些黑客就在這個過程當中加入本身的代碼執行)遍歷
最後一個參數地址 (最高地址)總結
倒數第二個參數地址 (次之)黑客
。。
第一個參數 (參數列表最低地址)
函數返回值
函數執行代碼
例子:
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;
總結:讀取可變參數的過程其實就是在堆棧中,使用指針,遍歷堆棧段中的參數列表,從低地址到高地址一個一個地把參數內容讀出來的過程.