當前位置:
首頁 > 知識 > C加加如何實現可變參數函數的調用?

C加加如何實現可變參數函數的調用?


可變參數定義:


我們學習C語言時最經常使用printf()函數,但我們很少了解其原型。其實printf()的參數就是可變參數,想想看,我們可以利用它列印出各種類型的數據。下面我們來看看它的原型:


intprintf( const char* format, ...);


它的第一個參數是format,屬於固定參數,後面跟的參數的個數和類型都是可變的(用三個點「…」做參數佔位符),實際調用時可以有以下的形式:


printf("%d",i);

printf("%s",s);


printf("thenumber is %d ,string is:%s", i, s);


例如printf函數:它接受一個格式字元串,並且後面跟隨任意指定的參數,實際上它的實現要依賴於一個標準 C 庫 ,stdandard argument(標準參數) 的意思。


2可變參函數的設計:


標準頭文件stdarg.h提供了一套對可變參函數的實現機制,所以編寫可變參函數需要包含該頭文件。#include


C中變長實參頭文件stdarg.h提供了一個數據類型va-list和三個宏(va-start、va-arg和va-end),用它們在被調用函數不知道參數個數和類型時對可變參數表進行測試,從而為訪問可變參數提供了方便且有效的方法。va-list是一個char類型的指針,當被調用函數使用一個可變參數時,它聲明一個類型為va-list的變數,該變數用來指向va-arg和va-end所需信息的位置。


VC++中va_list、va_start、va_arg、va_end的細節:


va_list:


typedef char * va_list;


va_start:

#define _INTSIZEOF(n)


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


#define _crt_va_start(ap,v)


( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) )


作用:va_start的第一個參數是va_list變數指針,第二個參數應該傳可變參函數的最後一個固定形參,其作用是使va_list類型的指針指向變參函數的第一個可變形參.想要一起學習C 的可以加裙二四八八九四四三零,有很多大神一起學習交流,有資源,然後可以訂閱轉發一下


va_arg:


#define _crt_va_arg(ap,t)


( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )


作用:va_arg宏從上面的實現代碼可以看出,其具有兩個作用:第一是使va_list類型的指針指向下一個可變參數;第二是獲取va_list類型指針指向的值.va_arg宏的第一個參數是va_list變數,第二個參數是待獲取的參數的數據類型.


va_end:

#define _crt_va_end(ap)


( ap = (va_list)0 )


作用:va_end主要是對資源釋放,使va_list類型的指針不指向任何內存.


從上面對va_list、va_start、va_arg、va_end的分析可以知道,要編寫可變長參數函數,我們首先要定義一個va_list類型的變數arg_ptr,然後使用va_start(arg_ptr,arg_list)宏使變數arg_ptr指向對一個可變形參,然後才能使用va_arg獲取各個變參的值,在最後一定要使用va_end宏釋放資源.


實現變參函數的三種方法:


在C++中實現一個變參函數的方法有三種:第一種方法,將函數形參聲明為C++11新標準中的initializer_list標準庫類型;第二種方法繼承自C語言,形參聲明為省略符,函數實現時用參數列表宏訪問參數;最後一種方法利用C++泛型特性,聲明一個可變參數模板來實現。


1可變參數宏


實現步驟如下:


.函數原型中使用省略號;


.函數定義中創建一個va_list變數;

.初始化va_list變數;


.訪問參數列表;


.完成清理工作;


上述步驟的實現需要使用到四個宏:va_list、va_start(va_list, arg)、va_arg(va_list, type)、va_end(va_list)這些宏在頭文件stdarg.h中聲明定義。因此使用時需要包含該頭文件。


以下代碼使用可變參數宏實現一個函數sum,該函數接受任意個數的整形實參,返回這些實參的和。(忽略可能存在的整形溢出)



C加加如何實現可變參數函數的調用?



使用這種方法需要注意一下幾點:


1. 函數原型中,省略號必須在參數列表的末尾:也就是說,在函數原型中參數列表省略號的右邊不能再出現確定參數;


2. 運行時,函數必須能夠根據已有信息(既有約定,或確定實參)確定可變參數的具體個數與類型:函數定義需要知道可變參數的具體類型、個數,這些信息是在運行時確定的,那麼顯然應該由實參來確定。在上面的例子中count傳遞了可變參數的個數,而參數類型則是既有約定(整形);

3. 使用完成時需要用va_end()做清理工作,可變參數宏可能使用了動態分配的內存,忘記執行清理操作有可能導致內存泄漏等問題;


4. 可變參數宏只能實現順序訪問可變參數,無法後退訪問,但是可以在清理操作完成後重新使用va_start初始化va_list變數,重新遍歷形參表.


2initializer_list標準庫類型


實現步驟如下:


.函數原型中使用實例化initializer_list模板代表可變參數列表;


.使用迭代器訪問initializer_list中的參數;


.傳入實參寫在{}之內。


以上步驟中使用到initializer_list。這是C++11新標準中引入的一個標準庫類型,與vector等容器一樣initializer_list也支持begin()和end()操作,返回指向首元素的迭代器和尾後迭代器。initializer_list在同名頭文件中聲明,其實現由編譯器支持。



C加加如何實現可變參數函數的調用?


使用這種方法需要注意一下幾點:


1. initializer_list在C++11中才被引入,這意味著在編譯時可能需要加上這個選項 -std=c++11 才能成功編譯。上述代碼中的auto關鍵字也是C++11的一部分;


2. 參數必須放在一組『{}』(大括弧)內,編譯器通過大括弧來將這組參數轉化為initializer_list.大括弧的的一組實參與initializer_list形參對應;


3. 函數原型initializer_list與普通形參無異。這表明形參列表中可以包含其他類型參數且位置不限,以下函數原型是正確的:


void func(char c, initializer_list il, double d);


4.同一個initializer_list中的參數具有相同的類型。本質上來說initializer_list是一個編譯器支持的容器類模板,同其他容器一樣,容器中的元素具有相同的類型。


使用這種方法的一個實例是C++11中vector的列表初始化構造函數。


3可變參數模板


一個可變參數模板(variadic template)就是一個接受可變數目參數的模板函數或模板類。


可變數目的參數被稱為參數包(parameter packet)。存在兩種參數包:模板參數包(表示零個或多個模板參數)和函數參數包(表示零個或多個函數參數)。

直觀的兩個概念:



C加加如何實現可變參數函數的調用?



利用可變參數模板實現可變參數函數的步驟如下:


. 編寫含有模板參數包和函數參數包的模板函數;


. 函數定義遞歸調用自己,每一步遞歸參數包中參數減一;


. 編寫處理邊界情況(參數包含有零個參數)的模板。


可變參數函數模板通常是遞歸的。第一步調用處理包中的第一個實參,然後用剩餘的實參調用自身。為了終止遞歸,我們還需要定義一個非可變參數的函數模板:



C加加如何實現可變參數函數的調用?


非可變參數版本的print負責終止遞歸併列印初始調用中的最後一個實參。對於最後一次遞歸調用print(42),兩個print版本都是可行的。但是,非可變參數模板比可變參數模板更特例化,因此編譯器選擇非可變參數版本。


使用這種方法需要注意的是:


1. 必須處理邊界情況。且如代碼注釋所示:應當首先定義處理邊界情況的模板。


2. 參數包在參數列表最右側,參數包只能從左至右展開?


3. 參數包能夠實現更加複雜的模板,更多內容參考C++ Primer(第五版)第16章相關內容。


這種實現方式的根本原理實際上與最初提到的重載是一致的。通過定義模板,讓編譯器根據實參類型自動生成對應的重載函數。



C加加如何實現可變參數函數的調用?



想要一起學習C 的可以加裙二四八八九四四三零,有很多大神一起學習交流,有資源,然後可以訂閱轉發一下

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 C加加 的精彩文章:

教程:c語言全局變數與函數調用,循環綜合測試
C語言鏈表與文件之學生成績管理系統
五分鐘學會炫酷C語言實現動態順序表
編程語言的選擇重不重要!

TAG:C加加 |

您可能感興趣

未來加拿大不加息!TD銀行已經主動降低了可變抵押貸款利率
什麼是可變幀率
Python函數的參數 默認參數 可變參數 關鍵字參數 命名關鍵字參數
區塊鏈實際上是不可變的嗎?
手機真能實現可變光圈?與電子光圈有何不同?
Python 函數式編程:不可變數據結構
手機為什麼要使用可變光圈?
阿里確認馬雲出讓主要可變利益實體控制權 但會繼續發揮影響力
命中處處充滿定數,也處處都有可變性!
AMD可變著色比率技術專利曝光:通過選擇性渲染提高幀數
廢舊鞋盒再利用,也可變的美麗又好看
Python面試之可變對象和不可變對象
大自然中痴迷變性上癮的魚類,堊鮨魚一天竟可變換性別二十次!
三星Note9評測:可變光圈加持,S Pen還能這樣玩?
心腸可變硬,也可以變軟的三大星座
Kotlin:命名參數、默認參數值、可變參數、局部函數
超大行李箱也可以登機啦!箱子可變大變小,還可以追蹤去向
新假面騎士ZI-O解禁情報曝光,形態多達10種,武器也可變換形態
看印度正在研製可變形戰機,隱身機六年後真的能飛嗎?
心若簡單,窮可變富,富則變貴