stdarg.h是c
語言中c
標準函式庫的標頭檔案,
stdarg
是由standard
(標準)
arguments
(引數)簡化而來,主要目的為讓函式能夠接收可變引數。
c++的
cstdarg
標頭檔案中也提供這樣的功能;雖然與
c的標頭檔案是相容的,但是也有衝突存在。可變引數函式
(variadic functions)
是stdarg.h
內容典型的應用,雖然也可以使用在其他由可變引數函式呼叫的函式(例如
vprintf)
。(參加
stdarg.h(vs2013
的win32平台)
的內容:
#pragma
once
#ifndef
_inc_stdarg
#define
_inc_stdarg
#include
#if!defined
(_win32)
#error
error: only win32 targetsupported!
#endif
/* !defined (_win32) */
#include
#ifdef
__cplusplus
extern
"c"
#endif
/* __cplusplus */
#endif
/* _inc_stdarg
它定義了四個巨集:
va_start,va_arg, va_end, va_copy
其中,va_start, va_arg,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
_intsizeof
(n) ( (
sizeof
(n) +
sizeof
(int
) - 1)& ~(
sizeof
(int
) - 1) )
使用這三個巨集時均會用到乙個引數
ap,它的型別為
va_list, va_list
的定義如下:
#ifdef
_m_cee_pure
typedef
system::argiterator va_list;
#else
/* _m_cee_pure */
typedef
char
* va_list;
#endif
/* _m_cee_pure */
在大多系統中,
va_list
相當於char*
,用於儲存可變引數的位址。
下面介紹這三個巨集的作用: (
1)巨集va_start
(ap,v)
的作用是得到第乙個可變引數的位址,
_addressof
(v) +
_intsizeof
(v),這裡
_addressof
(v)為最後乙個固定引數的位址,
_intsizeof
(v)為這個固定引數實際占用位元組數,故相加後就得到了第乙個可變引數的起始位址了,
va_start
(ap,v)
用來將ap
指向第乙個可變引數的位址。
(2)巨集va_arg
(ap,t)
的作用是把
ap指向當前可變引數的下乙個可變引數的位址,並返回當前可變引數的內容,我們來看它的實現。
( *(t *)((ap +=
_intsizeof
(t)) -
_intsizeof
(t)) )
首先執行最裡面的括號的內容:
(ap +=
_intsizeof
(t))
,該表示式等價於
ap = ap+
_intsizeof
(t),將ap
指向當前引數的下乙個引數,那麼表示式變為
( *(t *)(ap -
_intsizeof
(t)) ),
此時ap
已經指向
當前可變引數的下乙個可變引數,
ap -
_intsizeof
(t)得到的指標,指向了當前可變引數,但此處
ap沒有賦值操作,因此,
ap不變,仍指向
當前可變引數的下乙個可變引數。
( *(t *)(ap -
_intsizeof
(t)) )
將返回當前可變引數的值;
(3)巨集va_end
(ap)
的作用是將
ap指標關掉,它的實現為
( ap = (
va_list
)0 )
,使得ap
不再指向可變引數的位址,相當於給它置為
null
,以免出現危險,在自己定義的可變函式的末尾用到,用於釋放
va_start
以及va_arg
占用的記憶體。
下面是乙個簡單的
int型別變數相加的例子:
#include
#include
#include
void
******_add_fun(
intstart
, ...)
} while
(true);
printf(
"sum: %d\n"
, sum);
va_end
(arg_ptr);
return;
} intmain(
intargc
,char
*argv)
最後介紹巨集
va_copy。
va_copy
(apd, aps)
使用較少,用於將
va_list
型別變數
aps賦值給
apd。此時,我們可能會有疑問了,直接通過
va_list apd = aps
這種方式賦值不就行了嗎?幹嘛需要定義乙個
va_copy
的巨集呢?
通過查閱資料發現(參考
)在有些系統中,
va_list
型別為乙個長度為
1的陣列,此時需要採用
*apd=*aps
的賦值方式,故封裝乙個
va_copy
巨集來自適應地完成賦值工作。相應地,每乙個
va_copy(apd,aps)
需要乙個
va_end(apd)
來配對使用,將
apd置為
null
,以免出現危險。
下面是**
c++學習**(
)上給出的乙個例子,用於列印輸入的
int值:
/* va_copy example */
#include
/* printf, vprintf*/
#include
/* malloc */
#include
/* strlen, strcat */
#include
/* va_list, va_start, va_copy, va_arg, va_end */
/* print ints until a zero is found: */
void
printints(
intfirst
, ...)
va_end
(vl_count);
/* allocate storage for formatstring: */
buffer = (
char
*)malloc(strlen(format)*count + 1);
buffer[0] =
'\0';
/* generate format string: */
for(; count>0; --count)
/* print integers: */
printf(format,
first);
vprintf(buffer, vl);
va_end
(vl); }
intmain()
結果為[10] [20] [30] [40] [50] [0]
可變引數函式和巨集函式
格式化字串,可變引數函式需要借助於va list va start va arg va end巨集,可變引數巨集需要借助於 變參1 最後乙個可見引數型別是int int add int n,int main int add int n,該函式返回 n 個 int 數的和,n個int,跟在第乙個引數後...
可變引數函式定義及其陷井
一 關於可變引數的函式定義方法 某些情況下希望函式的引數個數可以根據需要確定。典型的例子有大家熟悉的函式printf scanf 和系統呼叫execl 等。那麼它們是怎樣實現的呢?c編譯器通常提供了一系列處理這種情況的巨集,以遮蔽不同的硬體平台造成的差異,增加程式的可移植性。這些巨集包括va sta...
可變引數函式定義及其陷井
一 關於可變引數的函式定義方法 某些情況下希望函式的引數個數可以根據需要確定。典型的例子有大家熟悉的函式printf scanf 和系統呼叫execl 等。那麼它們是怎樣實現的呢?c編譯器通常提供了一系列處理這種情況的巨集,以遮蔽不同的硬體平台造成的差異,增加程式的可移植性。這些巨集包括va sta...