C语言中不用宏va_list,va_start,va_arg写处理变长参数的函数 | ||||||
---|---|---|---|---|---|---|
| ||||||
http://blog.sina.com.cn 2006年09月07日10:54 随机过程 | ||||||
| ||||||
C语言中变长参数(va_list,va_start,va_arg)沉思录
一.引言:
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;
}
/* File mytest.c ends here *******************/
代码有点长,其实很简单,只是机械地对main()函数中的print()函数中的6个参数进行处理,依次打印上面的6个参数(注意,我没有用格式化符号,带格式话符号的处理函数我将在下面给出),去掉注释,在Linux AS4.2 gcc-3.4.4中编译,运行结果如下:
/************************
Hello 33.600000 aWorld *************************/
与预想的完全一致。说明print()函数对变长参数的理解是正确的。
|
帮助理解可变参数的应用【转贴】
最新推荐文章于 2024-09-11 01:15:07 发布