关于c语言可变参数详解

link:http://blog.chinaunix.net/space. ... blog&id=2808215
TABLE
1.介绍
2.使用
  2.1 代码实例,简单体会。
  2.2三个主要的宏
  2.3可变参数的个数确定
  2.4重扫描
  2.5 更多的代码实例
3.注意事项/限制
4.参考资源


1.介绍
        c语言有一个强大的功能,就是它允许定义可接受可变参数列表的函数。如:
#include
int printf(const char * fmt, ...);
        "..."代表多个参数,可变。

        当定义带有可变参数的函数时,需要的头文件是:. 其内定义了三个宏va_start,va_arg,va_end

和一个数据类型va_list来实现我们的需求。


2.使用

2.1 代码实例,简单体会。
  1. filename: sum_n_nums.c
  2. #include   
  3. /* 
  4. *功能:求n个整数的加和,并返回 
  5. *输入: 
  6. *       int n,欲相加的数的个数 
  7. *       ... 可变参数 
  8. * 返回: 
  9. *       double, 加和的结果 
  10. */  
  11.   
  12. double sum_n_nums(int n, ...)  
  13. {  
  14.     double  isum;  
  15.     int     i;  
  16.     va_list ap;//va_list 类型  
  17.   
  18.     va_start(ap, n); //初始化ap,定位  
  19.     for (i = 0, isum = 0; i < n; i++) {  
  20.         isum += va_arg(ap, int); //一次取得可变参数  
  21.     }  
  22.     va_end(ap);//清理  
  23.   
  24.     return isum;  
  25. }  
  26.   
  27. int  
  28. main()//测试用例  
  29. {  
  30.     printf("%.2f\n" ,sum_n_nums(10 ,123 ,123 ,5434 ,123 ,  
  31.        123 ,5434 ,123 ,123 ,5434 ,23));  
  32.     return 0;  
  33. }#include   
  34. /* 
  35. *功能:求n个整数的加和,并返回 
  36. *输入: 
  37. *       int n,欲相加的数的个数 
  38. *       ... 可变参数 
  39. * 返回: 
  40. *       double, 加和的结果 
  41. */  
  42.   
  43. double sum_n_nums(int n, ...)  
  44. {  
  45.     double  isum;  
  46.     int     i;  
  47.     va_list ap;//va_list 类型  
  48.   
  49.     va_start(ap, n); //初始化ap,定位  
  50.     for (i = 0, isum = 0; i < n; i++) {  
  51.         isum += va_arg(ap, int); //一次取得可变参数  
  52.     }  
  53.     va_end(ap);//清理  
  54.   
  55.     return isum;  
  56. }  
  57.   
  58. int  
  59. main()//测试用例  
  60. {  
  61.     printf("%.2f\n" ,sum_n_nums(10 ,123 ,123 ,5434 ,123 ,  
  62.        123 ,5434 ,123 ,123 ,5434 ,23));  
  63.     return 0;  
  64. }
复制代码
2.2三个主要的宏

va_list

        它是一个适合保存va_start, va_arg, va_end所需信息的类型。如要访问不同的参数,那么调用函数要声明

一个va_list类型的数据对象。(常用ap作为其名字)。

va_start宏

        概述
        #include
         void va_start(va_list ap, parmN);

        无返回值
        说明

        在访问所有为命名的参数之前要调用宏va_start.它对ap进行初始化,也就是对可变参数进行定位。

参数parmN是函数定义(在...之前的那个)可变参数表中最右边的固定参数的标识符。例如:

        int printf(const char *fmt, ...);
        fmt 就是parmN.所以一个函数必须至少声明一个固定的参数。       
        注意:

        如果parmN用register存储类别, 一个函数或者数组类型,或者和应用默认的参数提升的类型

不兼容的参数累声明,那么这种行为未定以。

va_arg宏

        概述
        #include
        type va_arg(va_list ap, type)
        返回值

            第一次调用va_start后,对va_arg的第一次调用返回的是parmN制定的参数后面的参数值,

后面的调用一次返回剩下的参数值。

        说明

        宏va_list展开为一个表达式,这个表达式的类型和值跟调用的下一个参数相同。va_arg 的每一次

调用都会修改ap,这样后面的参数值就会依次返回。

        注意
        如果实际不存在下一个参数,或者类型和实际存在的下一个参数类型不兼容,那么这种行为未定以。
va_end宏
        概述
        #include
        void va_end(va_list ap);
       无返回值
        说明
        促进函数的正常返回,进行整理工作。可能会对ap进行修改, 这样ap就不在有用了。
        注意
        如果没用执行相应的va_start,或者没又在返回之前调用va_end, 那么这种行为未定以。

2.3可变参数的个数确定

        因为参数个数是可变的,所以函数执行的时候确定传进来的参数个数是很重要的。va_arg

宏处理不存在的参数行为为定义,我们不能用while( va_arg(ap, char *) != NULL) 

类似的形式来确定结束条件。一下提出两类方法:

        1.用最后一个固定参数对可变参数的个数进行确定。

        例1

        int printf(const char *fmt, ...);

        给定了fmt,那么后面可变参数的个数就确定了。如fmt = “a is %d, b is %f, and c is %s \n", 

通过控制格式,我们就可以确定需要传递,三个可变参数。



        例2
        int sum_n_nums(int n, ...);//传n个数,然后返回n个数的和。

        可变参数的个数通过n来确定。


        2.将可变参数的最后一个参数作为结束条件
        例
#include
        int execl(const char *pathname, const char *arg0, ...);

        在使用这个函数的时候要求传递可变参数的最后一个参数需要是 NULL,也就是(char *) 0.

这样我们就可以用 while (va_arg(ap, char *) != NULL)的形式来确定结束条件。

        推荐的方式是,在每一次循环扫描内都执行一次va_start,va_end.推荐的方式是,

在每一次循环扫描内都执行一次va_start,va_end.


2.4重扫描

        推荐的方式是,在每一次循环扫描内都执行一次va_start,va_end.
2.5 更多的代码实例
  1. filename: minprintf.c
  2.     #include   
  3.     #include   
  4.     /* 
  5.      * 功能: printf函数的简体版 
  6.      */  
  7.       
  8.       
  9.      void minprintf(char *fmt, ...)  
  10.     {  
  11.         va_list ap;  
  12.         char *p, *sval;  
  13.         int ival;  
  14.         double dval;  
  15.       
  16.         va_start(ap, fmt);  
  17.         for (p = fmt; *p; p++) { //通过fmt来控制可变参数(第一种方法)。  
  18.             if (*p != '%') {  
  19.                 putchar(*p);  
  20.                 continue;  
  21.             }  
  22.             switch (*++p) {  
  23.                 case 'd':  
  24.                     ival = va_arg(ap, int);  
  25.                     printf("%d", ival);  
  26.                     break;  
  27.                 case 'f':  
  28.                     dval = va_arg(ap, double);  
  29.                     printf("%f", ival);  
  30.                     break;  
  31.                 case 's':  
  32.                     for (sval = va_arg(ap, char *); *sval; sval++)  
  33.                         putchar(*sval);  
  34.                     break;  
  35.                 default:  
  36.                     putchar(*p);  
  37.                     break;  
  38.             }  
  39.         }  
  40.         va_end(ap);  
  41.     }  
  42.     //无测试用例  
复制代码
  1. filename: cat_strs.c
  2.     #include   
  3.     #include   
  4.     #include   
  5.     #include   
  6.       
  7.     /* 
  8.      *功能:和并多个字符串,然后返回。 
  9.      *输入: 
  10.      *    char *first: 合并的第一个字符串 
  11.      *    ...  后继的多个字符串,要以(char *) 0结束 
  12.      *返回: 
  13.      *     char *  合并后字符串指针 
  14.      *注意: 
  15.      *    合并后的字符串保存在静态空间; 
  16.      *    如果要合并的字符串总长度 大于 静态太分配的 CAT_BUF_LEN 那么函数出错,返回。 
  17.      *    传递的可变参数一定以NULL结束 
  18.      */  
  19.       
  20.      char *cat_strs(char *first, ...)  
  21.     {  
  22.     #ifndef CAT_BUF_LEN  
  23.     #define CAT_BUF_LEN 1024  
  24.     #endif  
  25.         static char result[CAT_BUF_LEN];  
  26.         va_list ap; //va_list 类型  
  27.         char *pr;  
  28.         int len_sum;  
  29.         char *ptmp;  
  30.       
  31.         memset(result, '\0', sizeof (result));//初始化静态空间  
  32.           
  33.         va_start(ap, first);//初始化ap  
  34.         // 判断合并的字符串总长度是否大于CAT_BUF_LEN  
  35.         while ((ptmp = va_arg(ap, char *)) != NULL)  //第二种方法控制结束条件  
  36.             len_sum += strlen(ptmp);  
  37.         len_sum += strlen(first);  
  38.         if (len_sum > CAT_BUF_LEN -1) {  
  39.             fprintf(stderr, "cat_strs: the CAT_BUF_LEN is not enought!\n");  
  40.             va_end(ap);  
  41.             exit(EXIT_FAILURE);  
  42.         }  
  43.         va_end(ap);  
  44.       
  45.         va_start(ap, first); //重扫描!  
  46.         pr = result;  
  47.         //合并字符串  
  48.         strcat(pr, first);  
  49.         while (ptmp = va_arg(ap, char *)) {  
  50.             strcat(pr, ptmp);  
  51.         }  
  52.         va_end(ap);  
  53.           
  54.         return result;  
  55.     }  
  56.       
  57.     int  
  58.     main(void) //测试用例  
  59.     {  
  60.         char *result;  
  61.       
  62.         result = cat_strs("start ", "sajfklefjwle" ,"wejflsjfdghwejl",   
  63.                "wejflsjfdghwejl", " end\n", NULL);//一定要以空指针结束  
  64.         fputs(result, stdout);  
  65.         exit(0);  
  66.     }  
复制代码
3.注意事项/限制

        1. 声明函数时必须至少有一个固定的参数,最后一个固定参数引用时候习惯上称为parmN。

        2. 必须在函数内执行va_start(ap, parmN),之后才能执行va_arg,或va_end。

        3 .对于va_arg,不能编写类型T,使得当它作为一个参数传递时范围会变大。例如,

不能用double代替float,int代替char等。


        可以编写的类型T,仅仅通过在其后面加一个*就可以转换为相应的指针类型。

        4 .前面执行了va_start,那么必须后面执行va_end.一旦执行了va_end,那么不能再执行va_arg。

除非再次执行va_start, 进行重扫描。


4.参考资源

        《c标准库》,P.J.Plauger 著,卢红星 徐明亮 霍建同 译, 2009年第1版

        《c程序设计语言 第2版》,Brian W.Kernighan/ Dennis M.Ritchie 著, 徐宝文 李志 译, 2009年第2版

《unix 环境高级编程 第2版》,W。Richard Stevens Stephen A。Rago 著, 尤晋元 张亚英 戚正伟 译, 2006年第1版

你可能感兴趣的:(编程语言,c语言,可变参数函数)