可变参数函数
可变参数函数
- 一,什么可变参数函数?
- 二,实现机制
- 1,函数调用时的栈与栈帧
- 2,可变参数实现机制
一,什么可变参数函数?
普通函数:
声明时参数个数和类型都固定好,比如 int add(int a, int b),只能接收两个 int。
可变参数函数:
在声明时只写固定的“前导”参数,后面用 … 表示还有不定数量、也不定类型的参数。
有点抽象,下面直接上例子
int my_printf(UART_HandleTypeDef *huart, const char *format, ...)
{char buffer[512];//定义储存字符串的数组va_list arg;//定义可变参数类型int len;//储存字符串长度的变量va_start(arg, format);//初始化第一个参数len = vsnprintf(buffer, sizeof(buffer), format, arg);//格式化字符串va_end(arg);HAL_UART_Transmit(huart, (uint8_t *)buffer, (uint16_t)len, 0xFF);return len;
}
这是我们串口发送时使用的函数,第一个形参固定了是一个UART的句柄(包含UART的所有信息),后面的参数可以改变如:
my_printf(&huart1,"Value=%d\n", 42);
my_printf(&huart1,"A=%d, B=0x%02X, C=%.2f\n", 10, 0x5A, 3.14);
这里应该理解了为什么串口所有可变参数函数了吧
接下来讲讲可变参数函数的具体实现机制
二,实现机制
在讲可变参数函数之前,我们要先了解函数调用时的栈
1,函数调用时的栈与栈帧
栈存储了大量数据,当调用函数时会分出一部分空间,这部分空间就是栈帧,储存函数的信息(参数、局部变量、返回地址等)
注:这些信息会从右往左得挤进栈帧里。可变变量会跟在固定变量后面进入栈帧
2,可变参数实现机制
1,va_list 指针类型,用于指向可变参数列表中的当前位置。
va_list:
指针类型,在原代码中。arg就是用来储存可变变量地址的宏,它得类型就是va_list.
在串口功能中,我们需要访问可变变量,完成传输,arg就是用来储存这些可变变量的。
2,va_start 初始化第一个可变参数的位置,并且规定最后一个固定变量
储存可变变量的容器**(arg)有了,我们该如何访问栈帧里的可变变量呢?
va_start会访问最后一个固定变量的位置,再直接规定下一个位置为第一个可变变量。
arg 会被初始化并指向可变参数的开始位置,方便后续通过 va_arg 获取每个参数。
因为我们前面说了,可变变量是紧跟着固定变量进的栈。
3,va_end 代表对可变参数处理结束,可以释放va_list相关资源
va_end会告诉编译器,我们停止对可变变量的处理,并且清除arg