Chinaunix首页 | 论坛 | 博客
  • 博客访问: 87028
  • 博文数量: 8
  • 博客积分: 279
  • 博客等级: 二等列兵
  • 技术积分: 125
  • 用 户 组: 普通用户
  • 注册时间: 2011-09-04 16:26
个人简介

http://fishcried.com

文章分类
文章存档

2014年(4)

2011年(4)

分类: C/C++

2011-09-13 12:07:43

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 <stdio.h>
  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 <stdio.h>
  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 <stdio.h>
  3.     #include <stdarg.h>
  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 <stdio.h>
  3.     #include <stdlib.h>
  4.     #include <string.h>
  5.     #include <stdarg.h>
  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版
阅读(2286) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~