C语言可变参数函数及三个宏va_start、va_arg和va_end的使用
来源:互联网 发布:javascript考试题目 编辑:程序博客网 时间:2024/04/28 16:33
一、可变参数函数的实例
大家熟知的printf()函数声明如下:
int printf(const char * format, ...);
它除了有一个参数format固定以外,后面跟的参数的个数和类型是
可变的,例如我们可以有以下不同的调用方法:
printf("%d",i);
printf("%s",s);
printf("the number is %d ,string is:%s", i, s);
二、如何编写一个自已的可变参数函数.
查了一下,在<stdarg.h>中定义了三个宏va_start()、va_arg()和va_end()用于实现可变参数。
void va_start( va_list arg_ptr, prev_param );
type va_arg( va_list arg_ptr, type );
void va_end( va_list arg_ptr );
试编了一个:
#include <stdio.h>
#include <stdarg.h>
#define ENDING_INT 0
int SumAll(int number1,...) //把参数加总
{
va_list arg_pointer; //首先定义一个va_list型的变量,这个变量是指向参数的指针.
int current_number; //当前的数字
int total; //数字之和
//用va_start初始化变量arg_pointer,这个宏的第二个参数是第一个可变参数(一个固定的参数)
va_start(arg_pointer,number1);
total=number1;
do
{
//用va_arg返回后续的可变参数, 类型是 int
current_number=va_arg(arg_pointer,int);
total += current_number;
}
while (current_number!=ENDING_INT); //如果参数是结束标识(这里是ENDING_INT),则结束
va_end(arg_pointer); //结束参数列表
return total;
}
int main(int argc, char* argv[])
{
int n;
n=SumAll(100,200,ENDING_INT); //返回结果是300
printf("%d /n",n);
n=SumAll(100,200); //由于没有结束标识,返回结果不确定
printf("%d /n",n);
}
因为va_start, va_arg, va_end等定义成宏,所以可变参数的类型和个数需要由程序代码控制。
一般来说,设一个结束标识,这里是 ENDING_INT。用它来识别不同参数的个数。
SumAll(100,200,ENDING_INT); //调用方式正确,返回结果是300
SumAll(100,200); //调用方式不正确,由于没有结束标识,返回结果不确定。
三、理解va_start、va_arg和va_end
看一下<stdarg.h>中宏的定义
定义:typedef char * va_list;
理解:va_list 就是一个指针,指向参数列表。
定义:#define _INTSIZEOF(n) ((sizeof(n)+sizeof(int)-1)&~(sizeof(int) - 1) )
定义:#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )
理解:va_start 宏, 就是把ap赋值为参数v起始的参数列表的下一个参数
定义:#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
理解:va_arg宏,就是把ap赋值为下一个参数
定义:#define va_end(ap) ( ap = (va_list)0 )
理解:va_end宏,就是把ap赋值为空(0)
从va_*的实现可以看出,充分运用指针,把C语言的灵活特性表现得淋漓尽致。当然,用不好也容易出错。
va_*中,为了得到所有传递给函数的参数,需要用va_arg依次遍历。但是有两个要求:
(1)要确定参数的类型。
一般来说,各个参数的类型是一样的。
(2)要有结束标志。如果没有结束标志,va将按默认类型依次返回内存中的内容,直到访问到非法内存而出错退出。
所以上述的调用 SumAll(100,200); 返回的结果是不确定的。
四、再写一个参数类型是 char * 的 可变参数函数.
#include <stdio.h>
#include <string.h>
#include <alloc.h>
#include <stdarg.h>
#define ENDING_STRING NULL
//把多个字符串连接起来
char *StrCat(char *src,...)
{
va_list va;
const char *src_pointer;
char *dest; /* 结果字符串 */
size_t dest_size; /* 结果字符串的大小*/
/* 计算字符串的大小 */
va_start (va, src); /* 开始变长参数处理 */
src_pointer = src;
dest_size = 1;
while (src_pointer!=ENDING_STRING) /* ENDING_STRING == NULL */
{
dest_size += strlen (src_pointer);
src_pointer = va_arg (va, char *); /* 取下一个参数 */
}
va_end (va); /* 结束变长参数处理 */
/* 申请内存 */
dest = malloc( dest_size );
if (dest == NULL) return (NULL);
/* 逐个复制字串到结果字符串 */
va_start (va, src); /* 开始变长参数处理 */
src_pointer = src;
dest [0] = '/0'; /* 先设置为空串 */
while (src_pointer!=ENDING_STRING) /* ENDING_STRING == NULL */
{
strcat (dest, src_pointer); /* 复制字串到结果字符串 */
src_pointer = va_arg (va, char *);
}
va_end (va); /* 结束变长参数处理 */
return (dest);
}
int main(int argc, char* argv[])
{
int n;
char *s;
s=StrCat("hello"," ","world",NULL);
printf("%s /n",s); //返回结果是 hello world
}
嗯,还是比较好用的,千万不要忘记: 调用时要加上结束标识符哦。
可能要问,为什么 printf() 函数调用时没有结束标识符呢?
了解了一下 printf()的源码,我是这样理解的。
printf(char *format,...) 中,在format参数中,就可以判断出后续参数的个数和类型,因此不需要结束标识符就可以知道参数的个数了。比如:
printf( "%s %d", "hello", 1);
"%s %d"表明后续参数个数为2个,第一个是 string类型,第二个是 int 类型。
试一下,如果写成这样
printf( "%s %d %d", "hello", 1);
"%s %d %d"表明后续参数个数为3个,实际上是两个,由于缺了一个,返回结果是不可确定的。
呵呵,调用printf()是要小心出错。
- C语言可变参数函数及三个宏va_start、va_arg和va_end的使用
- C语言函数之可变参数原理:va_start、va_arg及va_end !!!!!!和printascii在kernel启动前的应用
- C语言利用va_list、va_start、va_end、va_arg宏定义可变参数的函数
- C语言可变参数 va_start, va_arg, va_end使用介绍
- C语言可变参数va_start、va_arg、va_end的用法
- C语言函数之可变参数原理:va_start、va_arg及va_end
- C语言函数之可变参数原理:va_start、va_arg及va_end
- C语言函数之可变参数原理:va_start、va_arg及va_end
- C语言函数之可变参数原理:va_start、va_arg及va_end
- C/C++中va_list,va_start,va_arg,va_end等可变参数宏的使用
- C语言中的可变参数:va_list ,va_start,va_arg,va_end
- C语言可变参数,va_arg、va_list,va_start,va_end,_INTSIZEOF
- C语言:va_start、va_end、va_arg 实现可变长参数
- 深入C语言可变参数(va_arg,va_list,va_start,va_end,_INTSIZEOF)
- C语言可变参数--va_list、va_start、va_arg、va_end
- 深入C语言可变参数(va_arg,va_list,va_start,va_end)
- 函数可变参数va_list、va_start、va_arg、va_end原理及使用方法
- 可变参数列表的函数调用 va_list va_start va_arg va_end
- 《云计算核心技术剖析》迷你书连载一 – 首席的推荐和前言
- 《云计算核心技术剖析》迷你书连载二 - 云计算的架构
- [记录]vi学习地址
- 《云计算核心技术剖析》迷你书连载三 – 云计算的商业模式
- Android之基于HTTP协议的下载 (转)
- C语言可变参数函数及三个宏va_start、va_arg和va_end的使用
- 《云计算核心技术剖析》迷你书连载四 – 云的4种模式
- UltraJSON——Python 的极速 JSON 编解码器
- Can't read [proguard.ClassPathEntry@1a0c10f] (No such file or directory)
- android Gallery镜像倒影特效
- 黑马程序员Java类中的实例成员与静态成员
- android Gallery镜像倒影特效另一种方法
- fedora 15中file_get_contents、soap无法使用的处理
- 快速排序算法范例