1 回答

TA貢獻(xiàn)1844條經(jīng)驗(yàn) 獲得超8個(gè)贊
#include <stdarg.h> // 必須包含的頭文件
int Add(int start,...) // ...是作為占位符
{
va_list arg_ptr; // 定義變參起始指針
int sum=0; // 定義變參的和
int nArgValue =start; //
va_start(arg_ptr,start); // arg_ptr指向第一個(gè)變參
do
{
sum+=nArgValue; // 求和
nArgValue = va_arg(arg_ptr,int); // arg_ptr指向下一個(gè)變參
}
while(nArgValue != 0); // 判斷結(jié)束條件;結(jié)束條件是自定義為=0時(shí)結(jié)束
va_end(arg_ptr); // 復(fù)位指針
return sum;
}
函數(shù)的調(diào)用方法為Add(1,2,3,0);這樣,必須以0結(jié)尾,因?yàn)樽儏⒑瘮?shù)結(jié)束的判斷條件就是讀到0停止。
解釋:
所使用到的宏:
void va_start( va_list arg_ptr, prev_param );
type va_arg( va_list arg_ptr, type );
void va_end( va_list arg_ptr );
typedef char * va_list;
#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 )
1、首先把va_list被定義成char*,這是因?yàn)樵谖覀兡壳八玫腜C機(jī)上,字符指針類型可以用來(lái)存儲(chǔ)內(nèi)存單元地址。而在有的機(jī)器上va_list是被定義成void*的
2、定義_INTSIZEOF(n)主要是為了某些需要內(nèi)存的對(duì)齊的系統(tǒng).這個(gè)宏的目的是為了得到最后一個(gè)固定參數(shù)的實(shí)際內(nèi)存大小。在我的機(jī)器上直接用sizeof運(yùn)算符來(lái)代替,對(duì)程序的運(yùn)行結(jié)構(gòu)也沒(méi)有影響。(后文將看到我自己的實(shí)現(xiàn))。
3、va_start的定義為 &v+_INTSIZEOF(v) ,這里&v是最后一個(gè)固定參數(shù)的起始地址,再加上其實(shí)際占用大小后,就得到了第一個(gè)可變參數(shù)的起始內(nèi)存地址。所以我們運(yùn)行va_start(ap, v)以后,ap指向第一個(gè)可變參數(shù)在的內(nèi)存地址,有了這個(gè)地址,以后的事情就簡(jiǎn)單了。
這里要知道兩個(gè)事情:
⑴在intel+windows的機(jī)器上,函數(shù)棧的方向是向下的,棧頂指針的內(nèi)存地址低于棧底指針,所以先進(jìn)棧的數(shù)據(jù)是存放在內(nèi)存的高地址處。
(2)在VC等絕大多數(shù)C編譯器中,默認(rèn)情況下,參數(shù)進(jìn)棧的順序是由右向左的,因此,參數(shù)進(jìn)棧以后的內(nèi)存模型如下圖所示:最后一個(gè)固定參數(shù)的地址位于第一個(gè)可變參數(shù)之下,并且是連續(xù)存儲(chǔ)的。
|--------------------------|
| 最后一個(gè)可變參數(shù) | ->高內(nèi)存地址處
|--------------------------|
|--------------------------|
| 第N個(gè)可變參數(shù) | ->va_arg(arg_ptr,int)后arg_ptr所指的地方,
| | 即第N個(gè)可變參數(shù)的地址。
|--------------- |
|--------------------------|
| 第一個(gè)可變參數(shù) | ->va_start(arg_ptr,start)后arg_ptr所指的地方
| | 即第一個(gè)可變參數(shù)的地址
|--------------- |
|------------------------ --|
| |
| 最后一個(gè)固定參數(shù) | -> start的起始地址
|-------------- -| .................
|-------------------------- |
| |
|--------------- | -> 低內(nèi)存地址處
(4) va_arg():有了va_start的良好基礎(chǔ),我們?nèi)〉昧说谝粋€(gè)可變參數(shù)的地址,在va_arg()里的任務(wù)就是根據(jù)指定的參數(shù)類型取得本參數(shù)的值,并且把指針調(diào)到下一個(gè)參數(shù)的起始地址。
因此,現(xiàn)在再來(lái)看va_arg()的實(shí)現(xiàn)就應(yīng)該心中有數(shù)了:
#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
這個(gè)宏做了兩個(gè)事情,
①用用戶輸入的類型名對(duì)參數(shù)地址進(jìn)行強(qiáng)制類型轉(zhuǎn)換,得到用戶所需要的值
②計(jì)算出本參數(shù)的實(shí)際大小,將指針調(diào)到本參數(shù)的結(jié)尾,也就是下一個(gè)參數(shù)的首地址,以便后續(xù)處理。
(5)va_end宏的解釋:x86平臺(tái)定義為ap=(char*)0;使ap不再 指向堆棧,而是跟NULL一樣.有些直接定義為((void*)0),這樣編譯器不會(huì)為va_end產(chǎn)生代碼,例如gcc在linux的x86平臺(tái)就是這樣定義的. 在這里大家要注意一個(gè)問(wèn)題:由于參數(shù)的地址用于va_start宏,所以參數(shù)不能聲明為寄存器變量或作為函數(shù)或數(shù)組類型. 關(guān)于va_start, va_arg, va_end的描述就是這些了,我們要注意的 是不同的操作系統(tǒng)和硬件平臺(tái)的定義有些不同,但原理卻是相似的.
- 1 回答
- 0 關(guān)注
- 158 瀏覽
添加回答
舉報(bào)