1、可變參函數的原理 ios
C/C++函數的參數是存放在棧區的,而且參數的入棧是從參數的右邊開始,即最後一個參數先入棧,而第一個參數最後才入棧,因此,根據棧的後進先出性質,函數總能找到第一個參數。因此,可變參函數的實現必須可以從已知參數中獲取到函數所須要參數的個數; 函數
例如printf函數,第一個參數就是一個格式串,然後面所須要的參數個數可以從格式串中獲得。 測試
2、可變參函數的設計 google
標準頭文件stdarg.h提供了一套對可變參函數的實現機制,因此編寫可變參函數須要包含該頭文件。#include<stdarg.h> spa
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的細節: 指針
typedef char * va_list;
#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類型的指針指向變參函數的第一個可變形參. code
#define _crt_va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
做用:va_arg宏從上面的實現代碼能夠看出,其具備兩個做用:第一是使va_list類型的指針指向下一個可變參數;第二是獲取va_list類型指針指向的值.va_arg宏的第一個參數是va_list變量,第二個參數是待獲取的參數的數據類型. orm
#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宏釋放資源.
三、可變長參數函數的實現
分析:在上文的設計分析中,對於va_arg宏的使用有兩個比較關鍵的點,第一個是可變形參的參數個數;第二就是可變形參的參數類型,由於va_arg要根據參數的類型尋址;若知道參數的個數和參數的類型,則可變參函數的實現較爲簡單;不然,咱們要編寫一些邏輯去判斷實現.
實例1:實現一個可變參表的求和函數
int sum(int n, ...) { va_list arg_ptr = NULL; int i = 0, nRes = 0; va_start(arg_ptr, n); for(; i < n; ++i) { int temp = va_arg(arg_ptr, int); nRes += temp; } va_end(arg_ptr); return nRes; }
說明:該實例的實現比較簡單,函數的固定參數代表函數參數的個數,其次,參數的類型固定爲int型.
實例2:實現本身的printf函數
void myPrintf(char * strFormat, ...) { if(NULL == strFormat) return; char strInfo[1000] = {0}; va_list arg_ptr = NULL; va_start(arg_ptr, strFormat); vsprintf(strInfo, strFormat, arg_ptr); va_end(arg_ptr); fputs(strInfo, stdout); }
說明:雖然函數參數的個數和參數是未知的,可是使用了vsprintf函數,實現的代碼也比較簡單,至於對vsprinf函數不瞭解的能夠去baidu、google
完整的代碼:
#include <iostream> #include <stdarg.h> int sum(int n, ...) { va_list arg_ptr = NULL; int i = 0, nRes = 0; va_start(arg_ptr, n); for(; i < n; ++i) { int temp = va_arg(arg_ptr, int); nRes += temp; } va_end(arg_ptr); return nRes; } void myPrintf(char * strFormat, ...) { if(NULL == strFormat) return; char strInfo[1000] = {0}; va_list arg_ptr = NULL; va_start(arg_ptr, strFormat); vsprintf(strInfo, strFormat, arg_ptr); va_end(arg_ptr); fputs(strInfo, stdout); } int main() { myPrintf("%d\n", sum(3, 45, 15, 45, 100)); return 0; }