浅谈C语言参数可变函数的实现
1.需要的頭文件:stdarg.h
? 需要的宏:va_start(a,b) ? ?va_arg(a,b) ? ?va_end(a)
? 需要的類型別名: va_list
2.基本用法
? (1)寫函數頭?
1 return_type function_name(first_argument,...);? eg1:
1 int fun1(int a,...); //right 2 void fun2(double b,...); //right 3 char *fun3(int a, double b,...); //right 4 int printf(const char *format,...); //right 5 int wrong_function(...); //wrong? 參數數目可變的函數,其必須明確定義至少一個參數(且必須放在最前面),之后跟...表示剩下的參數數目、類型是不確定的。
? (2)定義va_list變量(定義幾個?根據需求而定)
? eg2:
1 va_list parg;? (3)va_start(parg,first_argument);這里parg是(2)中定義的va_list類型變量,first_argument是參數列表中的第一個參數(如果在參數列表中只明確定義 ? 了一個變量的話,那first_argument確實是參數列表中的第一個變量。但是,如果參數列表中明確定義了不止一個變量,那么first_argument指的是明確定義的 ? ? ? 最后一個變量).
? eg3:
1 int fun1(int a,...) 2 { 3 va_list pa; 4 va_start(pa,a); 5 //…… 6 }eg4:
1 int fun1(int a,int b,int c,…) 2 { 3 va_list pa; 4 va_start(pa,c); 5 //…… 6 }? (3)用va_arg(a,b)獲取不確定的參數
eg5:?
1 int a=va_arg(parg,int); //int是通過分析得出的 2 char *s=va_arg(parg,char *); //char *是通過分析得出的?
? 分析得出?什么意思呢?由于參數數目可變函數在傳遞參數的時候,并沒有傳遞參數的類型,也沒有傳遞參數的個數,所以需要以某種方式讓函數知道到底傳遞了多少個參數,到底每個參數是什么類型的。
? eg6:
1 prinf(“there are %d birds,%d dogs,10 %s”,5,3,”pigs”);? 輸出:there are 5 birds,3 dogs,10 pigs
? printf是怎么知道有3個參數,怎么知道參數分別是int int char*型的參數呢?其實可以發現,當我們剛開始用printf的時候,就有%d%c%s%f之類的奇怪東西跟著。在eg6中,因為有%d %d %s這3個帶%的東西,printf才知道有4(第一個參數+后面的三個參數)個參數;同樣因為%d %d %s,printf才知道是int,int,char*類型的變量。Printf正是通過分析%的數目以及%后面跟的字母,讓printf得以分析出參數的個數和類型。
? (4)va_end(parg);
? 這個沒什么好說的,只要記住函數最后要加這么一句話就對了。
3.完整的例子
eg7:
1 int add(int n,…)//計算n個數的和 2 { 3 int sum=0; 4 va_list parg; 5 6 va_start(parg,n); 7 while(n--)//循環n次 8 sum+=va_arg(parg,int); 9 10 va_end(parg); 11 return sum; 12 }?
eg8:
1 int my_easy_printf(char *format,…)//%d %c %s %h %u 2 { 3 int i_a; 4 double dbl_b; 5 unsigned u_c; 6 char *p_d; 7 //這里沒有定義char c_e,但是卻支持%c的操作,下面會講到 8 int print_arg=0; 9 va_list parg; 10 11 va_start(parg,format); 12 13 while(*format) 14 { 15 if(*format!=’%’) 16 putchar(*format); 17 else 18 switch(*++format) 19 { 20 case '%': 21 putchar(‘%’); 22 format++; 23 break; 24 case 'd': 25 case 'h': 26 i_a=va_arg(parg,int); 27 print_int(i_a);//這是一個輸出有符號整數的函數 28 print_arg++; 29 break; 30 case 'u': 31 u_c=va_arg(parg,unsigned int); 32 print_uint(u_c); //這是一個輸出無符號整數的函數 33 print_arg++; 34 break; 35 case 'c': 36 c_c=va_arg(parg,int); 37 putchar(c_c); 38 print_arg++; 39 break; 40 case 's': 41 p_d=arg(parg,char *); 42 while(*p_d) 43 putchar(*p_d++); 44 print_arg++; 45 break; 46 default: 47 va_end(parg); 48 return -1; 49 } 50 Format++; 51 } 52 va_end(parg); 53 return print_arg; 54 } 55 View Codeps:me_easy_printf雖然支持%c的輸出,但是并沒有定義char類型的變量,這是因為char類型變量在參數傳遞的過程中會進行類型提升,提升為int,相當于char類型變量都被類型轉換成int型變量了。
類似的
(unsigned/signed)char、short ---> (unsigned/signed)int
? float ---> double
4.va_list、va_start、va_arg、va_end的一種實現方式
這四個東西(宏和類型別名)的實現和具體環境有關,并非一成不變,下面內容僅供參考,在實際使用中不要自己編寫va_start、va_arg、va_end,不要自己聲明類型別名va_list,請使用官方提供版本。
(1)va_list
1 typedef char * va_listva_list 定義的變量用于存儲變量的指針
(2)va_start
1 va_start(parg,first_arg) (parg=(va_list)&first_arg+sizeof(first_arg))(3)va_arg
1 #define _SIZEOFINT(n) ((sizeof(n)+sizeof(int)-1)&~(sizeof(int)-1))//按sizeof(int)的整數倍不減對齊_SIZEOFINT(n)的目的是,求得一整數k,使
k%sizeof(int)==0 ----------1
k>=sizeof(n)?? ----------2
k是滿足1、2的最小整數
?? 這里以8位舉例_SIZEOFINT的實現方法,假設sizeof(int)為4,雖然int是32(16)位,但原理一樣:
假如我們要把m向4的整數倍對齊,那么只需讓m的二進制后兩位為0即可,即可滿足能被4(sizeof(int))整除的條件,方法是m&11111100,而~sizeof(int)-1即為11111100。但是不滿足條件2(除非n剛好能被4整除),因為把1變為0的話,數一定是減小的,所以要把(m+(4-1))&11111100才行,而4-1就是sizeof(int)-1,并且m就是sizeof(n)。綜上,為
((sizeof(n)+sizeof(int)-1)&~(sizeof(int)-1))
1 #define va_arg(parg,t) (parg=(va_list)&parg+SIZEOFINT(t)); 2 //取得第一個可變參數的指針,并存儲在parg中上面的_SIZEOFINT 主要是用于需要內存對齊的系統,倘若系統不需要內存對其,則
1 #define _SIZEOFINT(n) sizeof(n)?
1 #define va_arg(parg,t) (*(t*)((parg+=SIZEOFINT(t))-SIZEOFINT(t))) 2 //parg+=SIZEOFINT(t)使得parg指向下一個可變參數 3 //取得下一個可變參數的指針,并返回這個指針上的值(4)va_end
1 #define va_end(parg) (parg=(va_list)0) 2 //讓parg指向NULL,防止對指針的錯誤使用上面談的是參數數目和類型都不確定的函數,在C語言里,還有一種參數數目確定但是類型不確定的函數
比如文件操作函數fwrite的第一個參數,貌似不需要具體的類型,先看一下fwrite的聲明:
1 size_t fwrite(const void* buffer, size_t size, size_t count, FILE* stream);eg9:
1 //下面函數用于對兩個操作數進行+操作, 2 //如果mod為1,則進行整形+操作, 3 //如果mod為2則進行字符串操作,出錯返回ERROR,否則返回OK 4 int add(int mod,void *opd1,void *opd2,void*result) 5 { 6 char *popd1,popd2,presult; 7 8 if(1==mod) 9 { 10 * (int *)result=*(int *)opd1+*(int *)opd2; 11 return OK; 12 } 13 else if(2==mod) 14 { 15 popd1=(char *)opd1; 16 popd2=(char*)opd2; 17 presult=(char *)result; 18 19 while(*presult++=*opd1++); 20 presult--; 21 while(*presult++=*opd2++); 22 *presult=’\0’; 23 return OK; 24 } 25 else 26 return ERROR; 27 } 28 View Code?
本人萌新,錯誤難免,請叔叔們指教。
轉載于:https://www.cnblogs.com/inori/p/4972717.html
總結
以上是生活随笔為你收集整理的浅谈C语言参数可变函数的实现的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: JS 的 Promise详解
- 下一篇: java毕业设计软件测试过程管理源码+l