长沙网站开发公,上海建设项目环保验收公示网站,家教辅导培训网站建设,求网站建设详细过程参考#xff1a;https://blog.csdn.net/u013171226/article/details/121445507 目录什么是可变参数可变参数列表构成实现原理(va_list系列变参宏实现变参函数)代码示例函数通过固定参数指定可变参数个数#xff0c;打印所有变参值函数定义一个结束标记(-1)#xff0c;调用时… 参考https://blog.csdn.net/u013171226/article/details/121445507 目录什么是可变参数可变参数列表构成实现原理(va_list系列变参宏实现变参函数)代码示例函数通过固定参数指定可变参数个数打印所有变参值函数定义一个结束标记(-1)调用时通过最后一个参数传递该标记打印标记前所有变参值注意事项总结什么是可变参数
在C语言编程中有时会遇到一些参数可变的函数例如printf()、scanf()其函数原型为
int printf(const char* format,…)
int scanf(const char *format,…)对于 printf 它除了有一个参数 format 固定以外后面的参数其个数和类型都是可变的用三个点“…”作为参数占位符。
可变参数列表构成
任何一个可变参数的函数都可以分为两部分固定参数和可选参数。
至少要有一个固定参数其声明与普通函数参数声明相同可选参数由于数目不定(0个或以上)声明时用…表示。固定参数和可选参数共同构成可变参数函数的参数列表。
实现原理(va_list系列变参宏实现变参函数)
C语言中使用 va_list 系列变参宏实现变参函数此处va意为variable-argument(可变参数)。
x86平台VC6.0编译器中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) )//该宏返回当前变参值 使ap指向列表中的下个变参
#define va_arg(ap, type) ( *(type *)((ap _INTSIZEOF(type)) - _INTSIZEOF(type)) )//将指针ap置为无效结束变参的获取
#define va_end(ap) ( ap (va_list)0 )宏定义详解
_INTSIZEOF(n)_INTSIZEOF宏考虑到某些系统需要内存地址对齐。从宏名看应按照sizeof(int)即栈粒度对齐参数在内存中的地址均为sizeof(int)4的倍数。 例如若1≤sizeof(n)≤4则_INTSIZEOF(n)4若5≤sizeof(n)≤8则_INTSIZEOF(n)8。 va_start(ap,v)va_start宏首先根据(va_list)v得到参数 v 在栈中的内存地址加上_INTSIZEOF(v)即v所占内存大小后使 ap 指向 v 的下一个参数。在使用的时候一般用这个宏初始化 ap 指针v 是变参列表的前一个参数即最后一个固定参数初始化的结果是 ap 指向第一个变参。
va_arg(ap, type)这个宏取得 type 类型的可变参数值。首先ap _INTSIZEOF(type)即 ap 跳过当前可变参数而指向下个变参的地址然后ap-_INTSIZEOF(type)得到当前变参的内存地址类型转换后解引用最后返回当前变参值。
va_end(ap)va_end 宏使 ap 不再指向有效的内存地址。该宏的某些实现定义为((void*)0)编译时不会为其产生代码调用与否并无区别。但某些实现中 va_end 宏用于在函数返回前完成一些必要的清理工作如 va_start 宏可能以某种方式修改栈导致返回操作无法完成va_end 宏可将有关修改复原又如 va_start 宏可能为参数列表动态分配内存以便于遍历va_end 宏可释放此内存。因此使用 va_start 宏的函数中退出之前必须调用一次 va_end 宏。
代码示例
变参宏无法智能识别可变参数的数目和类型因此实现变参函数时需自行判断可变参数的数目和类型。所以我们就要想一些办法比如
1、显式提供变参数目或设定遍历结束条件2、显式提供变参类型枚举值或在固定参数中包含足够的类型信息(如printf函数通过分析format字符串即可确定各变参类型)3、主调函数和被调函数可约定变参的数目和类型4、…
函数通过固定参数指定可变参数个数打印所有变参值
#include stdarg.h
#include stdio.hvoid parse_valist_by_num(int arg_cnt, ...);int main(void)
{parse_valist_by_num(4,1,2,3,4);parse_valist_by_num(4,1,2,3); parse_valist_by_num(4,1,2,3,4,5); //多余的变参被忽略
}//第一个参数定义可变参数的个数
void parse_valist_by_num(int arg_cnt, ...)
{va_list p_args;va_start(p_args, arg_cnt);int idx;int val;for(idx 1; idx arg_cnt; idx){val va_arg(p_args, int);printf(第 %d 个参数: %d\n, idx, val);}printf(---------------\n);va_end(p_args);
}ubuntu编译运行结果如下 函数定义一个结束标记(-1)调用时通过最后一个参数传递该标记打印标记前所有变参值
#include stdarg.h
#include stdio.hvoid parse_valist_by_flag(int num_1, ...);int main(void)
{parse_valist_by_flag(1,-1);parse_valist_by_flag(1,2,3,5,-1);parse_valist_by_flag(-1);}//函数定义一个结束标记(-1)调用时通过最后一个参数传递该标记以结束变参的遍历打印。
//最后一个参数作为变参结束符(-1)用于循环获取变参内容
void parse_valist_by_flag(int num_1, ...)
{va_list p_args;va_start(p_args, num_1);int idx 0;int val num_1;while(val ! -1){idx;printf(第 %d 个参数: %d\n, idx, val);val va_arg(p_args, int); //得到下个变参值}va_end(p_args);printf(---------------\n);
}ubuntu编译运行结果如下 注意事项
va_arg(ap, type)宏中的 type 不可指定为以下类型
charshortfloat
在C语言中调用不带原型声明或声明为变参的函数时主调函数会在传递未显式声明的参数前对其执行缺省参数提升(default argument promotions)将提升后的参数值传递给被调函数。
提升操作如下
float 类型的参数提升为 double 类型char、short 和相应的 signed、unsigned 类型参数提升为 int 类型若 int 类型不能容纳原值则提升为 unsigned int 类型
总结