實現可變引數函式常用巨集及其作用

2021-07-24 08:09:09 字數 4667 閱讀 9961

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...