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 argumentpromotions)”。

同时,对可变长参数列表超出最后一个有类型声明的形式参数之后的每一个实际参数,也将执行上述提升工作。
提升工作如下:
——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、signedchar、unsignedchar
——short、unsignedshort
——signedshortshortint、signedshortint、unsignedshortint
——float

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

参考:

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

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

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

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

优质内容筛选与推荐>>
1、Asp.net中解决“请求超时”的问题
2、Linux下部署MySQL,大小写敏感踩坑记录
3、CN003 - Ping与TCP/IP的关系-4
4、lua转让C++书面DLL达到“热更新”
5、js apply使用


长按二维码向我转账

受苹果公司新规定影响,微信 iOS 版的赞赏功能被关闭,可通过二维码转账支持公众号。

    阅读
    好看
    已推荐到看一看
    你的朋友可以在“发现”-“看一看”看到你认为好看的文章。
    已取消,“好看”想法已同步删除
    已推荐到看一看 和朋友分享想法
    最多200字,当前共 发送

    已发送

    朋友将在看一看看到

    确定
    分享你的想法...
    取消

    分享想法到看一看

    确定
    最多200字,当前共

    发送中

    网络异常,请稍后重试

    微信扫一扫
    关注该公众号





    联系我们

    欢迎来到TinyMind。

    关于TinyMind的内容或商务合作、网站建议,举报不良信息等均可联系我们。

    TinyMind客服邮箱:support@tinymind.net.cn