c语言可变参函数探究
一、什么是可变长参数
可变长参数:顾名思义,就是函数的参数长度(数量)是可变的。比如 C 语言的 printf 系列的(格式化输入输出等)函数,都是参数可变的。下面是 printf 函数的声明:
int printf ( const char * format, ... );
可变参数函数声明方式都是类似的。
二、如何实现
C语言可变参数通过三个宏(va_start、va_end、va_arg)和一个类型(va_list)实现的,
void va_start ( va_list args, paramN );
参数:
args: 可变参数列表地址 
paramN: 确定的参数
功能:初始化可变参数列表(把函数在 paramN 之后的参数地址放到 args 中)。
void va_end ( va_list args );
功能:关闭初始化列表(将 args 置空)。
type va_arg ( va_list args, type );
功能:返回下一个参数的值。
va_list :存储参数的类型信息。
好了,综合上面3个宏和一个类型可以猜出如何实现C语言可变长参数函数:用 va_start 获取参数列表(的地址)存储到 args 中,用 va_arg 逐个获取值,最后用va_end 将 args置空。
#include <stdio.h>
#include <stdarg.h> #define END -1 int va_sum (int first_num, ...)
//省略号代表可变函数
{
// (1) 定义参数列表
va_list args;
// (2) 初始化参数列表
va_start(args, first_num); int result = first_num;
int temp = ;
// 获取参数值
while ((temp = va_arg(args, int)) != END)
{
result += temp;
} // 关闭参数列表
va_end(args); return result;
} int main ()
{
int sum_val = va_sum(, , , , , END);
printf ("%d", sum_val);
return ;
}
三、 要点总结:
- 宏定义在 stdarg.h 中,所以使用时,不要忘了添加头文件。
- 注意相关宏的执行顺序。
- 设定一个参数结束标志(cplusplus 上说,va_arg 并不能确定哪个参数是最后一个参数)。
- 类型的匹配。
四、原理探究:
// 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) )
即:将函参在栈中的地址记录到va_list变量中,然后逐个读取变量的值;
五、总结:
c语言的可变长参数令人着迷,细想实现的思路,无非就是首先要知道参数在栈中,然后获取其值即可,其他就是细节的问题了;
c语言可变参函数探究的更多相关文章
- C语言中变参函数传参探究
		背景引入 近期在看一本书,叫做<嵌入式C语言自我修养>,写的内容对我帮助很大,是一本好书.在第6章,GNU C编译器扩展语法精讲一节,这本书给出了一些变参函数的例子: //1.变参函数初体 ... 
- Go语言:变参函数
		变参函数: 函数中形式参数的数目通常是确定的,在调用的时候要依次传入与形式参数对应的所有实际参数,但是在某些函数的参数个数可以根据实际需要来确定,这就是变参函数. Go语言支持不定长变参,但是要注意不 ... 
- C语言可变参函数分析
		code[class*="language-"], pre[class*="language-"] { color: rgba(51, 51, 51, 1); ... 
- c语言学习-指针探究
		1:指针定义格式:格式:变量类型 *变量名用途:指针变量用于储存地址(only),也就是根据地址值,访问对应的存储空间. 注意.int *p 只能指向int类型的数据: 例: int a = 20; ... 
- C语言变参函数/Variadic fucntion
		几个重要的 宏/类型 定义 Macros Defined in header <stdarg.h> va_start enables access to variadic function ... 
- c /c++变参函数(转)
		原文转自 https://blog.csdn.net/wwzcx/article/details/8940092 实现c/c++语言的变参函数. 变参函数 :void fun(para,...) 变参 ... 
- Is this Sentence Difficult? Do you Agree? -paper
		术语: 数据lemma:词根,词元(词的基本形式,如名词单数或动词的不定式形式) content words:实词part-of-speech:词性object-relative clauses an ... 
- 《深入理解C#(第3版)》
		<深入理解C#(第3版)> 基本信息 原书名:C# in depth 作者: (英)Jon Skeet 译者: 姚琪琳 丛书名: 图灵程序设计丛书 出版社:人民邮电出版社 ISBN:978 ... 
- PWN学习之格式化字符串漏洞
		目录 PWN学习之格式化字符串漏洞 格式化输出函数 格式化字符串漏洞 漏洞利用 使程序崩溃 栈数据泄露 任意地址内存泄漏 栈数据覆盖 任意地址内存覆盖 PWN学习之格式化字符串漏洞 格式化输出函数 可 ... 
随机推荐
- thinkphp学习笔记8—命名空间
			原文:thinkphp学习笔记8-命名空间 新版本(3.2)中采用命名空间的方式定义和加载类库文件,解决多个模块之间的冲突问题,并实现了更加高效的自动加载机制. 需要给类库定义所在的命名空间,命名空间 ... 
- handlebar的一些用法——个人使用总结
			handlebar的一些用法 概述与介绍 Handlebars 是 JavaScript 一个语义模板库,通过对view和data的分离来快速构建Web模板.它采用"Logic-less t ... 
- Hadoop它——跑start-all.sh时间namenode不启动
			转载请注明出处:http://blog.csdn.net/l1028386804/article/details/46353211 近期遇到了一个问题,运行start-all.sh的时候发现JPS一下 ... 
- 最长公共子序列问题 (LCS)
			给定两个字符串S和T.求出这两个字符串最长的公共子序列的长度. 输入: n=4 m=4 s="abcd" t="becd" 输出: 3("bcd&qu ... 
- 豆瓣api之OAuth认证
			豆瓣api通过OAuth允许第三方应用访问用户数据,所以OAuth认证就是我们整个project的基础了. OAuth认证听起来挺神秘,其实挺简单的. 现在的大型网站的开放平台的认证几乎都是采用OAu ... 
- robot framework用python扩展编写自定义library
			我的utils.py文件 #!/usr/bin/env python #-*- coding:utf8 -*- __version__ = '0.1' import sys reload(sys) s ... 
- 冒泡排序与简单选择排序——Java实现
			1.冒泡排序 1)原理说明:反复遍历要排序的数列,一次比較两个元素,假设他们的顺序错误就把他们交换过来.走訪数列的工作是反复地进行直到没有再须要交换,也就是说该数列已经排序完毕. 2)代码实现: pa ... 
- 通用Key-Value存储系统的存储管理策略解析
			Key-Value存储作为NoSQL存储的一种常见方式,提供了比SQL数据库更好的可扩展性和读写性能. 比方当前开源最热门的Memcached和Redis:淘宝的Tair.腾讯的Cme ... 
- 透过【百度地图API】分析双闭包问题
			原文:透过[百度地图API]分析双闭包问题 摘要: 有位API爱好者问到,昨天的教程里为什么不使用for循环?他使用for循环后,也发现代码无效.这是什么原因? ------------------- ... 
- 【百度地图API】——如何用label制作简易的房产标签
			原文:[百度地图API]--如何用label制作简易的房产标签 摘要: 最近,API爱好者们纷纷说,自定义marker太复杂了!不仅定义复杂,连所有的dom事件都要自己重新定义.有没有快速简易创建房产 ... 
