C语言函数的可变长度参数va_arg剖析
2014-09-18 15:46
417 查看
C语言的printf函数,可以输入不同的参数,一般通过函数重载的方式实现这种函数名相同而参数不同的机制,但考虑到printf的所有可能性,函数重载很明显解决不了这个问题,printf 和vsprintf
使用了可变长度参数来实现这种机制。
先看一个典型作用
void f(int flag, ...){
va_list args;
va_start(args, flag);
int n = va_arg(args, int);
}
C语言的函数调用机制
C语言的函数调用机制,是将函数参数与函数调用后下一条指令的地址都压入栈中,然后跳到函数的入口地址。
例如
void func(int param1, double param2,int param3){ }
int main(){
func(3, 1.2, 4);
printf("Over\n"); //设指令地址为0x1234
return 0;
}
执行f(3, 1.2, 4)的函数调用,进入func函数时的堆栈如下:
这样,通过param1的地址就可以计算出param2与param3的地址:
¶m2 = ((char*)¶m1) + sizeof(param2)
所以...只是对后面的参数的一种省略的写法,只要我们在程序中进行相应的计算与类型转换,就可以得到每个参数的值。
模拟实现
//方法一
void func(int param1, double param2, int param3){
double *p2 = (double*) ((char*) param1) + sizeof(param1);
int *p3 = (int*) ((char*)param1)+sizeof(param1)+sizeof(param2);
}
//方法二
void func(int param1, double param2, int param3){
char * p;
p = (char*)¶m1 + sizeof(param1);//参数一的结尾
p+= sizeof(double);//参数二的结尾
double *p2 = (double*) (p - sizeof(double);
p+=sizeof(int);//参数三的结尾
int *p3 = (int*) (p-sizeof(int);
}
这样就可以猜测va_list va_start va_arg va_end的实现
va_list 声明一个指针变量,跟踪当前参数的指针;
va_start 初始化这个指针的位置,最开始指向第一个参数的结尾的位置
va_arg 则移动指针,指向一个新参数的位置,并返回当前值
void func(int param1, double param2, int param3){
va_list p;
va_start(p, param1);
double p2 = va_arg(double);
double p3 = va_arg(int);
}
再和方法二的实现进行对照
可以预测
va_list <-> char *
va_start <-> p=&beginparam
va_arg <-> x=*p, p+=sizeof(x)
va_end做了什么?
Linux的实现方法
查看Linux0.11的内核源代码,对va_list, va_start, va_arg 的实现如下:
1. va_list的实现没有差别,char*
typedef char *va_list;
2. va_start的实现
#define va_start(AP, LASTARG) \
(__builtin_saveregs (), &n
使用了可变长度参数来实现这种机制。
先看一个典型作用
void f(int flag, ...){
va_list args;
va_start(args, flag);
int n = va_arg(args, int);
}
C语言的函数调用机制
C语言的函数调用机制,是将函数参数与函数调用后下一条指令的地址都压入栈中,然后跳到函数的入口地址。
例如
void func(int param1, double param2,int param3){ }
int main(){
func(3, 1.2, 4);
printf("Over\n"); //设指令地址为0x1234
return 0;
}
执行f(3, 1.2, 4)的函数调用,进入func函数时的堆栈如下:
这样,通过param1的地址就可以计算出param2与param3的地址:
¶m2 = ((char*)¶m1) + sizeof(param2)
所以...只是对后面的参数的一种省略的写法,只要我们在程序中进行相应的计算与类型转换,就可以得到每个参数的值。
模拟实现
//方法一
void func(int param1, double param2, int param3){
double *p2 = (double*) ((char*) param1) + sizeof(param1);
int *p3 = (int*) ((char*)param1)+sizeof(param1)+sizeof(param2);
}
//方法二
void func(int param1, double param2, int param3){
char * p;
p = (char*)¶m1 + sizeof(param1);//参数一的结尾
p+= sizeof(double);//参数二的结尾
double *p2 = (double*) (p - sizeof(double);
p+=sizeof(int);//参数三的结尾
int *p3 = (int*) (p-sizeof(int);
}
这样就可以猜测va_list va_start va_arg va_end的实现
va_list 声明一个指针变量,跟踪当前参数的指针;
va_start 初始化这个指针的位置,最开始指向第一个参数的结尾的位置
va_arg 则移动指针,指向一个新参数的位置,并返回当前值
void func(int param1, double param2, int param3){
va_list p;
va_start(p, param1);
double p2 = va_arg(double);
double p3 = va_arg(int);
}
再和方法二的实现进行对照
可以预测
va_list <-> char *
va_start <-> p=&beginparam
va_arg <-> x=*p, p+=sizeof(x)
va_end做了什么?
Linux的实现方法
查看Linux0.11的内核源代码,对va_list, va_start, va_arg 的实现如下:
1. va_list的实现没有差别,char*
typedef char *va_list;
2. va_start的实现
#define va_start(AP, LASTARG) \
(__builtin_saveregs (), &n
查看全文
相关文章推荐
- C语言函数之可变参数原理:va_start、va_arg及va_end !!!!!!和printascii在kernel启动前的应用
- C语言(函数传递可变长度的参数列表)va_arg
- C语言函数之可变参数原理:va_start、va_arg及va_end
- C语言函数之可变参数原理:va_start、va_arg及va_end
- C语言函数可变长度参数剖析
- C语言函数之可变参数原理:va_start、va_arg及va_end
- C语言函数之可变参数原理:va_start、va_arg及va_end
- C++可变参数编程,va_start va_arg va_end 的使用和原理
- va_start,va_arg,va_end,va_list应用举例--实现可变参数的函数
- 理解可变参数va_list、va_start、va_arg、va_end原理及使用方法
- fprintf 的封装(vsprintf,va_start(), va_arg(), va_end()可变参数列表)
- 深入C语言可变参数(va_arg,va_list,va_start,va_end,_INTSIZEOF)
- 可变参数列表(va_list,va_arg,va_copy,va_start,va_end)
- C语言中可变参数的用法——va_list、va_start、va_arg、va_end参数定义
- 关于C/C++中可变参数的详细介绍(va_list,va_start,va_arg,va_end)
- 函数可变参数va_list、va_start、va_arg、va_end原理及使用方法
- 可变参数va_start,va_arg,va_end的用法
- 可变参数va_start, va_arg, va_end的用法
- 编写一个可变参数的C函数——头文件stdarg.h中宏va_start ,va_arg和va_end的应用
- 可变参数va_start, va_arg, va_end的用法