如何自定義可變引數函式
在我們編寫程式碼中,有時需要我們自定義可變引數函式,像庫函式中有pirntf,ioctl都是可變引數函式,如果我們要實現自定義可變引數,一般要實現像int ioctl(int fd, unsigned long request, ...)這種功能的。下面講解如何實現ioctl這個型別函式
1.通過分析printf函式:
1)typedef char *va_list;
2)#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) ) //計算n佔用大小
3)#define va_start(ap,v)( ap = (va_list)&v + _INTSIZEOF(v) ) //獲取第一個可變引數在棧地址(在棧中引數入參順序為從右至左,棧底為高地址,棧頂為底地址,&v+_INTSIZEOF(v) ,這裡&v是最後一個固定引數的起始地址,再加上其實際佔用大小後,就得到了第一個可變引數的起始記憶體地址。所以我們執行va_start (ap, v)以後,ap指向第一個可變引數在的記憶體地址
4)#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
/*這個巨集做了兩個事情,
①用使用者輸入的型別名對引數地址進行強制型別轉換,得到使用者所需要的值
②計算出本引數的實際大小,將指標調到本引數的結尾,也就是下一個引數的首地址,以便後續處理。*/
5)#define va_end(ap) ( ap = (va_list)0 )
2.模板:
void myioctl(int select,...)
{
va_list args;
va_start(args, select);//將args賦值為第一個可變引數地址
switch(select){
case 1:
//假設這個選項需要兩個可變參
int i=va_arg(args,int);
char c=va_args(args,char);
......
va_end(args);
case 2:
.....
}
va_end(args);
}
3.舉例子
#include<stdio.h>
#include<stdarg.h>//第一部分說明的那幾個巨集就在這個標頭檔案實現的
#if 0
#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
typedef char *va_list1;
#define va_start1(ap,v)( ap = (va_list1)&v + _INTSIZEOF(v) )
#define va_arg1(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
#define va_end1(ap) ( ap = (va_list1)0 )
#endif
void myioctl(int select,...)
{
va_list args;
int i;
int c;
char* d;
va_start(args, select);
switch(select){
case 1:
i=va_arg(args,int);
c=va_arg(args,int);
printf("test:i=%d,c=%d\n",i,c);
break;
case 2:
i=va_arg(args,int);
d=va_arg(args,char*);
printf("test:i=%d,c=%s\n",i,d);
break;
}
va_end(args);
return;
}
int main()
{
int a=0x34,b=0x23;
char* string="haode";
myioctl(0x1,a,b);
myioctl(0x2,a,string);
return 0;
}
疑問:很奇怪,我不用庫裡自帶的va_start,va_args(即不用標頭檔案stdarg.h),而是自定義上面的,居然不行,不知道怎麼回事。