深度剖析C語言可變引數列表

2021-07-08 11:04:36 字數 3241 閱讀 7397

void printfloats ( int amount, ...);
注:宣告中函式的第乙個引數名必須是可知的(如例amout引數名開始,而非直接...開始)因為要通過巨集確定其傳入引數起始位址及個數等問題。
/* va_start example */

#include

#include

void printfloats ( int

amount, ...)

va_end(vl);

//結束標誌,這個巨集只是看起來和va_start 對稱而已

printf ("\n");

}int main ()

3.可變引數的內部實現相關型別的原型(在**中右擊跳轉到定義):

typedef char *  va_list;

#define va_start  _crt_va_start

#define va_arg  _crt_va_arg

#define va_end  _crt_va_end

#define _crt_va_start(ap,v)  ( ap = (va_list)_addressof(v) + _intsizeof(v) )

#define _crt_va_arg(ap,t)    ( *(t *)((ap += _intsizeof(t)) - _intsizeof(t)) )

#define _crt_va_end(ap)      ( ap = (va_list)0 )

#define _addressof(v)   ( &reinterpret_cast(v) )

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

具體分析:

#define _addressof(v)   ( &reinterpret_cast(v) )

這個巨集通過型別轉換取得引數v的位址。也就是如上例子中的&amount

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

由引數的傳遞過程來看,這裡的n就是上邊的v也就是&

amount

那麼這個式子就是求引數的某個大小了。:

#define _intsizeof(n)    對於上式amount 為int型別有:

( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )

=((4         + 4           - 1) & ~(4           - 1) )

=((7                          ) & ~(3                )

=  0111  &  ~(0011)

=  0111  &  ~(1100)

=  0100 = 4(這不正是int的位元組對齊數麼)

#define _intsizeof(n)   假設傳入引數為char型別:

( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )

=((1         + 4           - 1) & ~(4           - 1) )

=((4                          ) & ~(3                )

=  0100  &  ~(0011)

=  0100  &  ~(1100)

=  0100 = 4(這不正是x86下的預設位元組齊數麼)

可看出這個只是什麼東東了?  它就是傳說中的位元組對齊數處理。由於傳入引數壓棧是由此規矩搞定,那麼現在使用我們也就需要用到他了。

#define _crt_va_start(ap,v)  ( ap = (va_list)_addressof(v) + _intsizeof(v) )

有以上的分析,對此式子有:ap =  (char*)

(&amount)+(amount引數的位元組對齊數)   //也就是下乙個變數的位址了。

所以,實際上,該表示式是反悔了當前位址的變數,而將ap指向下乙個位址。

#define _crt_va_end(ap)      ( ap = (va_list)0 )

這個就簡單了,設定ap = (char*)0;  也就是設定為空。其實這句是可有可無的,只是和start對稱,程式的完整性。。或者你也可以理解為他是安全著想(然而影響並沒有什麼)

至此,我們已經將所有相關的巨集進行解讀剖析完成。可變引數也就成為了我們使用的一大利器。

另外補充一點的是,這個引數中,最重要的就是對位元組對齊的處理了。。這個問題是平台相關的問題了。

這裡對應的系統情況下,預設對齊取得是int

.所以這也就解釋了另外乙個問題。(如下例)

以下是乙個簡單的程式片段

int add(char a,...)

{int val;

cout << &val << endl;

int main()

{int a = 'a';

cout << &a << endl;

char res = add('a','b','b','b','a',-1);

這個問題就是說,對於用到可變引數的地方,引數傳遞是按照每個引數分別計算位元組對齊((1補齊為4)+4=8),而後壓棧的,而非一起處理位元組對齊而壓棧(此情形時應當差距4(1+1--》補齊為4)位元組).

//注:這個真心有點坑。原來不能直接對於進行複製貼上進來,搞得我重新補充/

C語言可變引數列表

c語言中類似於printf這種型別的函式,在呼叫它們時我們傳入的引數的型別和數量都不是固定的,這就需要可變引數列表,要使用可變引數列表,要用到以下幾種巨集。include void va start va list ap,last type va arg va list ap,type void v...

(C語言)可變引數列表

c函式要在程式中用到以下這些巨集 void va start va list arg ptr,prev param type va arg va list arg ptr,type void va end va list arg ptr va list 用來儲存巨集va start va arg和v...

C語言 可變引數列表原始碼的剖析

對可變引數解析之前,我們先看乙個栗子 實現乙個函式可以求任意個引數的平均值。宣告乙個va list型別的變數arg,它用於訪問引數列表的未確定部分。這個變數是呼叫va start來初始化的。它的第乙個引數是va list的變數名,第2個引數是省略號前最後一位有名字的引數。初始化過程把arg變數設定為...