深度探索C语言函数可变长参数
来源:互联网 发布:淘宝店铺访客少怎么办 编辑:程序博客网 时间:2024/05/16 08:49
深度探索C语言函数可变长参数
独立博客链接:http://www.keepsimply.org/2012/08/18/deep-explore-c-va-arg/
作者:独酌逸醉
时间:2012.08.18
一、基础部分
1.1 什么是可变长参数
可变长参数:顾名思义,就是函数的参数长度(数量)是可变的。比如 C语言的 printf系列的(格式化输入输出等)函数,都是参数可变的。下面是 printf函数的声明:
int printf ( const char * format, ... );
可变参数函数声明方式都是类似的。
1.2 如何实现
C语言可变参数通过三个宏(va_start、va_end、va_arg)和一个类型(va_list)实现的,
void va_start ( va_list ap, paramN );
参数:
ap: 可变参数列表地址
paramN: 确定的参数
功能:初始化可变参数列表(把函数在 paramN之后的参数地址放到 ap中)。
void va_end ( va_list ap );
功能:关闭初始化列表(将 ap置空)。
type va_arg ( va_list ap, type );
功能:返回下一个参数的值。
va_list :存储参数的类型信息。
好了,综合上面3个宏和一个类型可以猜出如何实现C语言可变长参数函数:用va_start获取参数列表(的地址)存储到 ap中,用 va_arg逐个获取值,最后用 va_arg将 ap置空。
1.3 举例
按 Ctrl+C 复制代码
按 Ctrl+C 复制代码
1.4 使用注意事项
1. 宏定义在 stdarg.h中,所以使用时,不要忘了添加头文件。
2. 设定一个参数结束标志(cplusplus上说,va_arg并不能确定哪个参数是最后一个参数)。
3. 类型的匹配
4. 期待您的补充……
二、深入原理
“源码面前,一览无遗”!
以下源码,来自“..\Microsoft Visual Studio 10.0\VC\include”
1
2
3
4
5
6
7
8
9
10
11
// stdarg.h
#define va_start _crt_va_start
#define va_arg _crt_va_arg
#define va_end _crt_va_end
// vadefs.h
typedef char * va_list;
#define _crt_va_start(ap,v) ( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) )
#define _crt_va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
#define _crt_va_end(ap) ( ap = (va_list)0 )
#define _ADDRESSOF(v) ( &(v) )
#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
除了 _INTSIZEOF之外,其他都很好理解,举个例子吧:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/* 作者:独酌逸醉
* 时间:2012.08.18
* 功能:测试 _INTSIZEOF 宏
* IDE: CodeBlocks 10.05
*/
#include <stdio.h>
#include <stdarg.h>
int main ()
{
int i = 1;
float f = 0.0;
printf("_INTSIZEOF(i) = %d\n", (int)(_INTSIZEOF(i)));
printf("_INTSIZEOF(f) = %d\n", (int)(_INTSIZEOF(f)));
printf("_INTSIZEOF(\"Hello,world\") = %d\n", (int)(_INTSIZEOF("Hello,world")));
printf("sizeof(\"Hello,world\") = %d\n", sizeof("Hello,world") );
return 0;
}
输出结果:
1
2
3
4
_INTSIZEOF(i) = 4
_INTSIZEOF(f) = 4
_INTSIZEOF("Hello,world") = 12
sizeof("Hello,world") = 12
既然 sizeof和 _INTSIZEOF值一样,为什么不直接用 sizeof呢?干嘛要写的那么复杂?答案是为了字节对齐(无论32位还是64位机器,sizeof(int)永远代表机器的位数,明白了吧!^_^)
现在再去看变长参数的实现:其实就是把参数在栈中的地址记录到 ap中(通过一个确定参数 paramN确定地址),然后逐个读取值。
此时是否有一种豁然开朗的感觉?至少明白了许多,也清楚了很多。
三、知识扩展
可能大家也猜到了,我扩展要扩展什么了?!^_^
简单介绍两种函数调用约定
__stdcall (C++默认)
1. 参数从右向左压入堆栈
2. 函数被调用者修改堆栈
3. 函数名(在编译器这个层次)自动加前导的下划线,后面紧跟一个@符号,其后紧跟着参数的尺寸
__cdecl (C语言默认)
1. 参数从右向左压入堆栈
2. 参数由调用者清楚,手动清栈,被调用函数不会要求调用者传递多少参数,调用者传递过多或者过少的参数,甚至完全不同的参数都不会产生编译阶段的错误。
那么,变参函数的调用方式为(也只能是):__cdecl。
本来打算多写一点扩展的,又担心会文不符题,所以感兴趣的朋友可以去看参考资料中的文章,有一些介绍的很详细。
参考资料
1. http://www.cplusplus.com/reference/clibrary/cstdarg/va_start/
2. http://www.cplusplus.com/reference/clibrary/cstdarg/va_end/
3. http://www.cplusplus.com/reference/clibrary/cstdarg/va_list/
4. http://www.cplusplus.com/reference/clibrary/cstdarg/va_arg/
5. http://51hired.com/questions/13278?sort=oldest
6. http://www.cnblogs.com/diyunpeng/archive/2010/01/09/1643160.html
7. http://blog.csdn.net/huanjieshuijing/article/details/5822942
8. http://baike.baidu.com/view/1280676.htm
c语言变长参数函数
(2012-05-20 22:24:34)
转载▼
标签:
杂谈
1、 要在函数中包含可变参数,首先应该在头文件中包含<stdarg.h>,即#include <stdarg.h>。这个头文件声明了一个va_list类型和四个操作可变参数的函数:
void va_start(va_list ap, argN);
void va_copy(va_list dest, va_list src);
type va_arg(va_list ap, type);
void va_end(va_list ap);
2、 所有的操作主要围绕头文件中声明的va_list和四个宏(函数)va_start(),va_copy(),va_arg()和va_end()。
va_list:va_list变量将能够用来操纵整个可变参数列表;
va_start():va_start()被用来初始化va_list类型的参数ap,并且是ap指向第一个可选参数;后面的参数argN一般值得是可变参数前一个参数,对于有可变长参数,但是在可变长参数钱没有任何固定参数的函数,如int func(...)是不允许的。这是ANSI C所要求的,变参函数在...之前至少得有一个固定参数。这个固定参数将被传递给va_start(),然后用va_arg()和va_end()来确定所有实际调用时可变长参数的类型和值;
va_arg():这个宏能够返回ap指向的列表中的参数的下一个参数,每一次调用va_arg()都修改ap的值,这样就能正确的返回参数列表中的值。type参数是ap所指向的数据类型;
va_copy() :复制va_list类型的变量。每次调用va_copy,必须有相应的va_end调用。
va_end():每次调用va_start()和v_copy()之后都要调用va_end()来销毁变量ap,即将指针置为NULL;
3、 下面将举一个简单的例子来说明怎么编写自己的可变长参数的函数:
#include <stdio.h>
#include <stdarg.h>
void myprintf(const char *format, ...)
{
va_list ap;
char c;
va_start(ap, format);
while(c = *format++)
{
switch(c)
{
case'c':
{
charch = va_arg(ap, int);
putchar(ch);
break;
}
case's':
{
char*p = va_arg(ap, char *);
fputs(p,stdout);
break;
}
default:
putchar(c);
}
}
va_end(ap);
}
int main(void)
{
myprintf("c\ts\n","1","hello");
return 0;
}
- 深度探索C语言函数可变长参数
- 深度探索C语言函数可变长参数
- 深度探索C语言函数可变长参数
- C语言可变长参数函数
- 语言函数可变长参数
- C语言可变长参数函数与默认参数提升
- C语言可变长参数函数与默认参数提升
- C语言可变长参数函数与默认参数提升
- C语言可变长参数函数与默认参数提升
- C语言 可变长参数函数的实现
- C语言中的可变长参数(转载)
- C语言中的可变长参数
- C语言中的可变长参数
- C语言——可变长参数
- C语言可变长参数实现“多态”
- c语言中的可变长参数
- C语言可变长参数实现原理
- C语言可变长参数实现原理
- 禁止MAC特殊字符
- Windows下安装curl
- Android中Listview的getChildAt()只能更新当前显示在屏幕上的Item的解决办法
- 安卓模拟器没有生成SD卡的解决方法
- 《深入理解java虚拟机》之字节码执行引擎
- 深度探索C语言函数可变长参数
- 我的Java学习之路1——Eclipse调试Bug的方法
- Ubuntu通过PPTP协议使用VPN
- 《剑指Offer》面试题:扑克牌的顺子
- PostgreSQL Why checkpointer impact performance so much ? - 2
- OCdemo-06 Block
- 高端HTML个性家具定制企业网站建设案例
- Piotr's Ants(Uva 10881)
- 使用服务实现简单的音乐播放