C語言中不用巨集實現變長引數函式的原理及實現

2021-05-18 07:45:53 字數 2398 閱讀 6173

一、前言

我們通常編寫的函式都是引數固定的,多了少了都會有錯,但是有時候我們是不能確定預先需要多少個引數的,而變長引數函式恰恰就能解決我們的問題。在unix中,提供了變長引數函式的編寫方法,主要是通過va_list物件實現, 定義在檔案'stdarg.h'中,變長引數函式的編寫有乙個固定的模板,模板很簡單(見下**), 定義時, 變長引數列表通過省略號『...』表示, 因此函式定義格式為:

type 函式名(引數1, 引數2, 引數n, . . .);

變長引數函式模板:

變長引數函式例項:

執行結果如下:

[root]#gcc  -o  mysum  mysum.c

[root]#./mysum

sum(1,4) = 4

sum(2,4,8) = 12

[root]#

在上面的執行結果中已經可以看到對於同樣乙個函式mysum,我們兩次呼叫時傳入的引數不一樣,這在通常情況下編譯器會報錯,但現在由於使用了變長引數,所以可以正確的執行了。

二、巨集的定義

前面說過va_list, va_start, va_end都是巨集,網上查了下,關於這三個巨集的定義,各編譯器不大一樣,下面是通過網路得到的關於這三個巨集的定義: 

gcc中va_list的定義

#define char*  va_list   /* gcc中va_list等同char* */

gcc中三個巨集的定義如下:

#define va_start(ap, lastarg) ( /

ap = ((char *)& (lastarg) + /

__va_rounded_size(lastarg)))

通過對應第一節中的例項來分析一下這個巨集,ap對應的是乙個va_list物件,lastarg自然對應的是mysum函式中的第乙個引數i了,

上面的意思就是首先取得第乙個引數的首位址(通過(char *)& (lastarg)這段實現的 ),然後將第乙個引數首位址加上引數的大小(

通過__va_rounded_size(lastarg)實現的),最終執行下來的結果是使ap指向mysum函式中的第二個引數。

#define va_arg(ap, type) ( /

ap += __va_rounded_size(type), /

*((type *)(ap - __va_rounded_size(type))))

其實這個巨集的功能和上面的差不多,使ap指向當前引數的後面那個引數。因此,就可以通過乙個迴圈呼叫這個巨集,將所有引數讀出了

#define va_end(ap)              /* 沒有定義,沒有操作 */

有的編譯器這樣定義:

#define va_end(ap) ((void *)0)  /* 有定義,是空操作 */

三.不用巨集的處理變長引數實踐:

從上面對巨集的分析看中,了解到了巨集是如何來實現變長函式的,原理如下:

1、首先獲得第乙個引數的位址

3、按第2步的方式迴圈可以獲得第3、第4、第5……直到最後乙個引數位址

這是乙份從網上找到的不用巨集實現的變長引數函式,它實現的方式正是按上面的原理進行:

**有點長,其實很簡單,只是機械地對main()函式中的print()函式中的6個引數進行處理,依次列印上面的6個引數(注意作者沒有用格式化符號,帶格式話符號的處理函式我將在下面給出)

執行結果如下

/************************

hello

33.600000

aworld

*************************/

與預想的完全一致。說明按上面的原理對變長引數的理解是正確的。

四、後記

為什麼已經有巨集實現了這麼乙個變長引數函式的功能,我們還要去吃飽了撐的沒事幹,用函式來實現,何況在大多數情況下巨集的執行效率要高於函式(一般來說巨集產生較大的**,但是避免了函式呼叫的堆疊操作,所以速度會比較快),功能也沒別人強大,實現也不完善,這實在是吃力又不討好。不過畢竟這是自己去理解、去發現、去實現的東西,很簡單乙個道理,就像做菜一樣,雖然自己的手藝不一定比得上大廚,但終究是自己做的,吃起來自然要香於現成的。

http://blog.sina.com.cn/s/blog_3e7df0e5010005ip.html~type=v5_one&label=rela_nextarticle在此對作者表示由衷的感謝

C語言中變長引數的函式的實現

當我們定義乙個函式時,有可能無法確定引數的個數,這是就需要用到變長引數函式,就想標準c函式printf一樣。一般這樣宣告 void func char form,前面至少有乙個確定的引數。函式體內如何獲取這些引數呢?這就需要用到幾個巨集以及了解他們的原理。函式引數是以資料結構 棧的形式訪問 從右至左...

C語言中變長引數例項

include include define sum arg.sum my name is arg char sum char a,變長引數的實現基於的是 c 語言預設的 cdecl 呼叫慣例中 自右向左壓棧的傳遞方式 比如 sum a,b,c 的各個引數在 棧中的相對順序為 top of stac...

C語言變長引數實現

include include include 編寫可變長引數列表的函式案例 void minprintf char fmt,這個函式只處理格式字串和引數,格式的轉換則通過printf函式實現 省略號表示引數的數量和型別是可變的,省略號只能出現再參數列的尾部,minprintf不需要像printf ...