- 如何實現(xiàn)像printf()一樣的可變參數(shù)函數(shù)
一般用到以下幾個宏
va_list arg_ptr//此處為類型定義 不是宏
va_start(va_list arg_ptr, type);
type va_arg(va_list, type);
void va_end(va_list);
va這里指variavle arguments,這些宏定義于<stdarg.h>頭文件下猜谚,下面實現(xiàn)一個簡單函數(shù)打印所有參數(shù)的值:
#include <stdarg.h>
#include <stdio.h>
void myVarArgFun(int i...);
int main()
{
myVarArgFun(10);
myVarArgFun(10, 20);
myVarArgFun(10, 20, 30);
getchar();
}
void myVarArgFun(int i...)
{
va_list(args);
va_start(args, i);
printf(" %d ", i);
int j = va_arg(args, int);
while (j)
{
printf(" %d ", j);
j = va_arg(args, int);
}
va_end(args);
printf("\n");
}````
由以上可以看出誓斥,實現(xiàn)一個可變參數(shù)函數(shù)應(yīng)有一下步驟:
+ 首先在函數(shù)里定義一個va_list型的變量,這里是arg_ptr,這個變量是指向參數(shù)的指針.
+ 然后用va_start宏初始化變量arg_ptr,這個宏的第二個參數(shù)是第一個可變參數(shù)的前一個參數(shù),是一個固定的參數(shù).
+ 然后用va_arg返回可變的參數(shù),并賦值給整數(shù)j. va_arg的第二個參數(shù)是你要返回的參數(shù)的類型,這里是int型.
+ 最后用va_end宏結(jié)束可變參數(shù)的獲取.然后你就可以在函數(shù)里使用第二個參數(shù)了.如果函數(shù)有多個可變參數(shù)的,依次調(diào)用va_arg獲取各個參數(shù).
***
**可變參數(shù)在編譯器中的處理**
我們知道va_start,va_arg,va_end是在stdarg.h中被定義成宏的,由于1)硬件平臺的不同 2)編譯器的不同,所以定義的宏也有所不同,下面VC++中stdarg.h里x86平臺的宏定義摘錄如下(’/’號表示折行):
typedef char * va_list;
define _INTSIZEOF(n) /
((sizeof(n)+sizeof(int)-1)&~(sizeof(int) - 1) )
define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) ) #define va_arg(ap,t) /
( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
define va_end(ap) ( ap = (va_list)0 )
定義_INTSIZEOF(n)主要是為了某些需要內(nèi)存的對齊的系統(tǒng).C語言的函數(shù)是從右向左壓入堆棧的
***
**可變參數(shù)在編程中要注意的問題**
因為va_start, va_arg, va_end等定義成宏,所以它顯得很愚蠢,可變參數(shù)的類型和個數(shù)完全在該函數(shù)中由程序代碼控制,它并不能智能地識別不同參數(shù)的個數(shù)和類型.有人會問:那 么printf中不是實現(xiàn)了智能識別參數(shù)嗎?那是因為函數(shù)printf是從固定參數(shù)format字符串來分析出參數(shù)的類型,再調(diào)用va_arg的來獲取可 變參數(shù)的.也就是說,你想實現(xiàn)智能識別可變參數(shù)的話是要通過在自己的程序里作判斷來實現(xiàn)的.
***
**小結(jié):**
可變參數(shù)的函數(shù)原理其實很簡單,而va系列是以宏定義來定義的,實現(xiàn)跟堆棧相關(guān).我們寫一個可變函數(shù)的C函數(shù)時,有利也有弊,所以在不必要的場合,我們 無需用到可變參數(shù).如果在C++里,我們應(yīng)該利用C++的多態(tài)性來實現(xiàn)可變參數(shù)的功能,盡量避免用C語言的方式來實現(xiàn).