zoukankan      html  css  js  c++  java
  • va_list实现不定参数C函数

    void vltest(int i, float k, ...){
        va_list vl; //定义va_list变量vl,该变量是指向参数的指针
        va_start(vl, k); // 参数一:va_list变量vl;参数二:va_list变量vl中最后一个固定参数
        int j = va_arg(vl, int); // 参数一:va_list变量vl;参数二:可变参数的类型,返回值j即可变参数
        double m = va_arg(vl, double); // 同上
        unsigned long n = va_arg(vl, unsigned long); // 同上
        va_end(vl); // 结束可变参数的获取
        printf("i = %d, k = %.f, j = %d, m = %lf, n = %lu\r\n", i,k, j, m, n);
    }

    上述方法不能智能识别不同参数的个数和类型。

    如果想实现智能识别可变参数,比如printf,需要在自己的程序中作特殊处理,示例如下:

    #include <stdarg.h>
     2 
     3 void my_printf(const char* fmt, ... )
     4 {
     5     va_list ap;
     6     va_start(ap,fmt); /* 用最后一个具有参数的类型的参数去初始化ap */
     7     for (;*fmt;++fmt)
     8     {
     9         /* 如果不是控制字符 */
    10         if (*fmt!='%')
    11         {
    12             putchar(*fmt); /* 直接输出 */
    13             continue;
    14         }
    15         /* 如果是控制字符,查看下一字符 */
    16         ++fmt;
    17         if ('\0'==*fmt) /* 如果是结束符 */
    18         {
    19             assert(0);  /* 这是一个错误 */
    20             break;
    21         }
    22         switch (*fmt)
    23         {
    24         case '%': /* 连续2个'%'输出1个'%' */
    25             putchar('%');
    26             break;
    27         case 'd': /* 按照int输出 */
    28             {
    29                 /* 下一个参数是int,取出 */
    30                 int i = va_arg(ap,int);
    31                 printf("%d",i);
    32             }
    33             break;
    34         case 'c': /* 按照字符输出 */
    35             {
    36                 /** 但是,下一个参数是char吗*/
    37                 /*  可以这样取出吗? */
    38                 char c = va_arg(ap,char);
    39                 printf("%c",c);
    40             }
    41             break;
    43         }
    44     }
    45     va_end(ap);  /* 释放ap—— 必须! 见相关链接*/
    46 }

    在C语言中,调用一个不带原型声明的函数时:

    调用者会对每个参数执行“默认实际参数提升(default argument promotions)”。

    同时,对可变长参数列表超出最后一个有类型声明的形式参数之后的每一个实际参数,也将执行上述提升工作。
    提升工作如下:
    ——float类型的实际参数将提升到double
    ——char、short和相应的signed、unsigned类型的实际参数提升到int
    ——如果int不能存储原值,则提升到unsigned int

    然后,调用者将提升后的参数传递给被调用者。

    因此,my_printf是绝对无法接收到上述类型的实际参数的。

    上面的代码的38与39行,应该改为:

    int c = va_arg(ap,int);
    printf("%c",c);

    同理, 如果需要使用short和float, 也应该这样:

    short s = (short)va_arg(ap,int);
    float f = (float)va_arg(ap,double);

     

    总之,va_arg(ap,type)中的type绝对不能为以下类型:
    ——char、signed char、unsigned char
    ——short、unsigned short
    ——signed shortshort int、signed short int、unsigned short int
    ——float

    注:部分编译器,如笔者所测试的xCode4.3,输入上述错误类型后有警告提示。

     

    参考:

    http://jazka.blog.51cto.com/809003/232331

    http://www.cplusplus.com/reference/clibrary/cstdarg/va_start/

    可变长参数列表误区与陷阱——va_arg不可接受的类型

    可变长参数列表误区与陷阱——va_end是必须的吗?

  • 相关阅读:
    CreateDIBSection函数
    rand()和srand()GetTickCount函数用法
    PRIMARY LANGUAGE ID not a number
    videojs 动态加载视频
    [记录] nicescroll 在bootstrap tabs中工作
    [记录] js判断数组key是否存在
    li 水平排列并自动填满 ul
    [Laravel] 获取执行的Sql
    [Laravel]配置路由小记
    昨天冲动的搬到外面住了,oh yeah
  • 原文地址:https://www.cnblogs.com/pure/p/2476287.html
Copyright © 2011-2022 走看看