注册 登录  
 加关注
查看详情
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

随机过程

http://superware.blog.163.com

 
 
 

日志

 
 

C 语言中变长参数 (va_list,va_start,va_arg) 沉思录  

2012-04-29 17:15:08|  分类: misc |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
      本文解析了 C 语言中的变长参数列表的实质,并试探不用变长参数宏编写含有变长参数列表的函数。看这篇博文的人似乎还比较多,但由于新浪博客很不友善,博客网页对代码的支持很受伤,把其中很多东西给弄丢了。我现在搬家了,于是把以前的东西修改修改,以更好地为人民服务 

:) C 语言中变长参数 (va_list,va_start,va_arg) 沉思录 - superware - 随机过程


一、变长参数宏定义解析

      C 语言中关于变长参数的使用方法是如下的框架:
#include <stdio.h>
#include <stdart.h> /* 或者#include <vararg.h> */

int print (char * fmt, ...)
{
    va_list args;
    ...
va_start (args, fmt);

    /* do something here */

    va_end (args);

    /* do something here too */
}
是否可以不用宏而编写处理变长参数的函数呢?答案是肯定的,本文作了一些处浅探讨,不足之处望各位批评指正。

      我看了一下有关 va_list,、va_start、 va_end 宏的定义,各编译器不大一样,我着重研究了一下 gcc 的以上三个宏,并在不用三个宏的情况下编写了测试程序。测试过程和大家分享。gcc 中 va_list 的定义大致如下:
#define char* va_list   /* gcc中va_list等同char* */

gcc中三个宏的定义如下(经过我加工整理后):

#define va_start(AP, LASTARG) ( \
            AP = ((char *)& (LASTARG) + \
            __va_rounded_size(LASTARG)))

#define va_arg(AP, TYPE) ( \
            AP += __va_rounded_size(TYPE), \
            *((TYPE *)(AP - __va_rounded_size(TYPE))))

#define va_end(AP)              /* 没有定义,没有操作 */
有的编译器这样定义:

#define va_end(AP) ((void *)0) /* 有定义,是空操作 */


二、不用宏的处理变长参数实践:

       本人在分析了上面的宏后,在Linux、 i386、 gcc、 gas 平台的 Think Pad R50e 上不用上面的三个宏成功写了个测试 C 代码,并详细分析了该代码的汇编代码,先给出代码如下:

/*****************************************************
* File Name : mytest.c          
* Copyright by : Superware   
* Version : 0.01             
*****************************************************/


#include <stdio.h>            /* 我没包含 stdarg.h 或 vararg.h */

void print (char * fmt, ...)
{

    char * arg;               /* 变长参数的指针
                                相当于 va_list arg */
    int i;                    /* 接受int参数 */
    double d;                 /* 接受double参数 */
    char c;                   /* 接受char 参数*/
    char *s;                  /* 接受字符串 */

    printf ("%s", fmt);       /* 打印第一个参数 fmt串 */

    arg = (char *)&fmt + 4;   /* 相当于 va_start(arg, fmt)
                                这里的 +4 实际上是
                                sizeof(char *) 因为在IA32
                                中,所以我写了4 没有考虑移植,
                                为什么? 在下面解释,
                                注意这里加 4表示arg已经指向
                                第二个参数 */
    /* 打印第二个参数 */
    i = *(int *)arg;           /* 接受第二个参数,
                                为了直接了当,我硬性规定
                                print()函数的第二个
                                参数是整数,请看 */
                                main()函数中的print()
                                函数调用 */

    printf ("%d", i);         /* 打印地二个参数,是整数,
                                所以用格式"%d" */
    arg += sizeof(int);        /* 指向下一个参数(第三个参数),
                                为什么是加sizeof(int),
                                分析汇编码你就明白了 */
    /* 打印第三个参数 */
    d = *(double *)arg;       /* 以下的解释同地二个参数类似,
                                就不详细解释了 */
    printf ("%f", d);

    arg += sizeof(double);

    /* 打印第四个参数 */
    c = *(char *)arg;

    printf ("%c", c);

    arg += sizeof (char *);

    /* 打印第五个参数 */
    c = *(char *)arg;

    printf ("%c", c);

    arg += sizeof (char *);

    /* 打印第六个参数 */
    s = *(char **)arg;

    printf ("%s", s);

    arg += sizeof (char *);

    arg = (void *)0;           /* 使arg指针为 (void)0,
                                 实际上就上使无效,否则arg
                                  依然指向第六个参数,危险。*/
                               /* 相当于 va_end(arg) */
}
int main (void)
{
    print ("Hello\n", 3, 3.6, '\n', 'a', "World\n");
    return 0;
}

       代码有点长,其实很简单,只是机械地对 main( ) 函数中的 print( ) 函数中的 6 个参数进行处理,依次打印上面的 6 个参数(注意,我没有用格式化符号, 带格式话符号的处理函数我将在下面给出),去掉注释,在 Linux AS4.2、gcc-3.4.4 中编译,运行结果如下:

Hello
33.600000
aWorld

与预想的完全一致。说明 print( ) 函数对变长参数的理解是正确的。
  评论这张
 
阅读(182)| 评论(0)
推荐

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2018