C语言可变参数
使用printf等函数的时候函数原型是printf(const char* fmt, ...)
, 这一类参数的个数不限的函数是可变参数
使用
使用一个头文件stdarg.h, 主要使用以下的宏
typedef char * va_list;
// 把 n 圆整到 sizeof(int) 的倍数
#define _INTSIZEOF(n) ( (sizeof(n)+sizeof(int)-1) & ~(sizeof(int)-1) )
// 初始化 ap 指针,使其指向第一个可变参数。v 是变参列表的前一个参数
#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )
// 使用type进行一个类型的转换,
#define va_arg(ap, type) ( *(type *)((ap += _INTSIZEOF(type)) - _INTSIZEOF(type)) )
// /将指针 ap 置为无效,结束变参的获取
#define va_end(ap) ( ap = (va_list)0 )
C语言可变参数详解_c语音 可变参数-CSDN博客
在函数定义中创建一个 va_list 类型变量,该类型是在 stdarg.h 头文件中定义的。
使用 int 参数和 va_start() 宏来初始化 va_list 变量为一个参数列表。宏 va_start() 是在 stdarg.h 头文件中定义的。
使用 va_arg() 宏和 va_list 变量来访问参数列表中的每个项。
使用宏 va_end() 来清理赋予 va_list 变量的内存。
va_start(ap, last_arg):初始化可变参数列表。
ap
是一个va_list
类型的变量,last_arg
是最后一个固定参数的名称(也就是可变参数列表之前的参数)。该宏将ap
指向可变参数列表中的第一个参数。va_arg(ap, type):获取可变参数列表中的下一个参数。
ap
是一个va_list
类型的变量,type
是下一个参数的类型。该宏返回类型为type
的值,并将ap
指向下一个参数。va_end(ap):结束可变参数列表的访问。
ap
是一个va_list
类型的变量。该宏将ap
置为NULL
。
原理
实际是从栈里面依次获取下一个字符串的指针地址
这里分析这两个函数的汇编代码
可以看出多出来的参数是依次被压入栈中进行传递的, 所以使用的时候可以使用最后一个参数的地址以及大小推导出来下一个参数的位置, 这里之后的参数实际是按照字符串指针的格式存储的
这里使用的栈是向下增长的, 但是压入的顺序是从右向左
如果使用fun(a, b)实际的栈里面是
再来看前面库函数
typedef char * va_list;
// 把 n 调整到 sizeof(int) 的倍数(这一个是用来计数对齐的, 栈的存储是对齐的)
#define _INTSIZEOF(n) ( (sizeof(n)+sizeof(int)-1) & ~(sizeof(int)-1) )
// 可以通过(va_list)&v获取已知最后一个参数的地址加上它的大小就是第一个参数的位置
#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )
// 使用type进行一个类型的转换, 转换的是现在的参数的地址, ap+=以后会指向下一个的位置
#define va_arg(ap, type) ( *(type *)((ap += _INTSIZEOF(type)) - _INTSIZEOF(type)) )
// /将指针 ap 置为无效,结束变参的获取
#define va_end(ap) ( ap = (va_list)0 )
实现一个简单的sprintf(只处理%s)
void kernel_vsprintf(char * buf, const char *fmt, ...){
va_list args;
//记录第一个可变参数的位置
va_start(args, fmt);
//一个状态机
enum {NORMAL, READ_FMT} state = NORMAL;
char * curr = buf;
char ch;
while((ch = *fmt++)){
switch(state) {
case NORMAL:
if(ch == '%')
{
//需要处理这一个格式字符
state = READ_FMT;
}else{
//普通字符直接复制
*curr++ = ch;
}
break;
case READ_FMT:
if(ch == 's')
{
//需要填入一个字符串
//获取这一个字符串的地址
const char * str = va_arg(args, char *);
int len = strlen(str);
while(len--){
*curr++ = *str++;
}
}
state = NORMAL;
break;
}
}
}