《OOC》笔记(3)——C语言变长参数va_list的用法
《OOC》笔记(3)——C语言变长参数va_list的用法
C语言中赫赫有名的printf函数,能够接受的参数数目不固定,这就是变长参数。C#里也有params这个关键字用来实现变长参数。
printf("Hello Mozart!");
printf("Hello %s!", "Mozart");
printf("%d: Hello %s!", , "Mozart");
用C实现一个能接受变长参数的函数
举例如下。
#include <stdarg.h> int add(const char * testString, int x, ...)
{
printf("%s\n", testString);
va_list list;
va_start(list, x);
int result = ; for(;;)
{
int p = va_arg(list, int);
if(p == )
break;
result += p;
}
va_end(list); // cleanup , set 'lsit' to NULL
return result;
} /* error: ISO C requires a named argument before '...'
void add2(...)
{
}
*/ int main()
{
int result = add("test case 1: ", , , , , 5, );
printf("%d\n", result);
system("PAUSE");
return ;
} /*
This program print as follows:
test case 1:
15
*/
编写使用变长参数的函数步骤如下。
- 首先,引用stdarg.h。
- 然后,在函数声明中用"..."表示这个函数能够使用变长参数。
注意,在"..."前面至少要有一个普通的参数。(可能非标准C不需要,不过我们还是保守一点最好)
- 那么如何使用这些数目、类型都不确定的参数呢?
va_list类型的list变量可以遍历"..."中的参数。
用va_start()来初始化list变量。va_start()需要挨着"..."的左边那个参数名。(示例中的x)
va_arg()用于获取下一个参数值。这个参数值的类型你必须在编码时就能确定。
va_end()用于结束对list的遍历。之后你可以再次使用va_start()、va_arg()、va_end()来依次获取各个可变参数值。
注意事项
在add这个示例中,最后一个参数必须为0,add才能知道可变参数处理完毕。没有别的办法。也就是说,你不可能通过任何方式不借助外力就得知传进来的可变参数到底有几个。
在printf("%d, %s, %c", 1, "11", '1');函数中,printf会分析格式化参数"%d, %s, %c",它看到3个格式化输出符号,所以就认为传入了3个可变参数。如果你传入的多了或者少了,程序就可能出错。编译器无法检测这个错误。
list变量可以作为参数传递给其它函数。(如vprintf("xxx", list);)
在传递可变参数时,整型会作为int或long传递,float型会作为double传递。
va_arg()的第二个参数(示例中的int)不应太复杂。(这话很含糊)
总之,C语言中使用变长参数不是什么好的编程实践。能避免尽量避免。
原理是什么?
typedef char* va_list
#define va_start(ap,v)(ap=(va_list)&v+_INTSIZEOF(v))
#define va_arg(ap,t)(*(t*)((ap +=_INTSIZEOF(t))-_INTSIZEOF(t)))
#define va_end(ap) ( ap = (va_list)0)
va_list是在stdio.h中定义的类型。va_start、va_arg、va_end是三个宏定义。
va_start把((v的地址)+(v的长度))赋给list。根据函数调用时形参的内存布局,这样list就指向了第一个可变参数。(示例中的2)
每次调用va_arg都会获得当前参数值,并将ap指针指向下一个参数。
调用va_end会将ap重置为0。
所以这个可变参数的原理就是一个迭代器,它在函数栈的参数上移动以依次获取可变参数。
《OOC》笔记(3)——C语言变长参数va_list的用法的更多相关文章
- C语言变长参数实现
#include<stdio.h> #include<string.h> #include<stdarg.h> /***编写可变长参数列表的函数案例*/ /* vo ...
- 介绍C++11标准的变长参数模板
目前大部分主流编译器的最新版本均支持了C++11标准(官方名为ISO/IEC14882:2011)大部分的语法特性,其中比较难理解的新语法特性可能要属变长参数模板(variadic template) ...
- C++中的变长参数
新参与的项目中,为了使用共享内存和自定义内存池,我们自己定义了MemNew函数,且在函数内部对于非pod类型自动执行构造函数.在需要的地方调用自定义的MemNew函数.这样就带来一个问题,使用stl的 ...
- Scala 变长参数
如果Scala定义变长参数 def sum(i Int*), 那么调用sum时,可以直接输入sum(1,2,3,4,5) 但是不可以sum(1 to 5) 必须要将1 to 5 强制为seq sum( ...
- C++11变长参数模板
[C++11变长参数模板] C++03只有固定模板参数.C++11 加入新的表示法,允许任意个数.任意类别的模板参数,不必在定义时将参数的个数固定. 实参的个数也可以是 0,所以 tuple<& ...
- 【Unix环境高级编程】编写变长参数函数
文件的格式输入输出函数都支持变长参数.定义时,变长参数列表通过省略号'...'表示, 因此函数定义格式为: type 函数名(parm1, parm2,parmN,...); Unix的变长参数通过v ...
- java常量和变量的定义规则,变长参数的使用
首先是定义的一般规则,类名首字母全部大写,常量全部大写用下划线分隔,变量用驼峰形式.注意使用long赋值用L时不能写小写的L要写大写的,不然会和数字“1”傻傻分不清. 下面是举例: public cl ...
- Lua 变长参数(variable number of arguments)
lua变长参数 function add ( ... ) for i, v in ipairs{...} do print(i, ' ', v) end end add(1, 2, 'sdf') lu ...
- 【小白学Lua】之Lua变长参数和unpack函数
一.简介 Lua的变长参数和unpack函数在实际的开发中应用的还挺多的,比如在设计print函数的时候,需要支持对多个变量进行打印输出,这时我们就需要用到Lua中的变长参数和unpack函数了. 二 ...
随机推荐
- js(ext)中,设置[!!异步!!]上传的简单进度条
代码在updateHmis的历史记录中,此处存档 handler : function() { //显示进度条 Ext.MessageBox.wait('数据上传中...','提示'); //上传数据 ...
- 手机GPS为什么能在室内定位?
为什么手机在室内也能定位?大部分人知道手机会通过GPS进行定位,其实手机定位系统并不是和我们的RTK完全一样的,因为那样就无法解释为何在室内也能定位了,这里我来科普一下智能手机的那些定位方法. ...
- hdoj 1869 六度分离
Problem Description 1967年,美国著名的社会学家斯坦利·米尔格兰姆提出了一个名为“小世界现象(small world phenomenon)”的著名假说,大意是说,任何2个素不相 ...
- LeetCode OJ-- Single Number II **@
有一列数,其中有1个数出现了1次,其它数都出现了3次,求这个数. class Solution { public: int singleNumber(int A[], int n) { ) ; ; ; ...
- ajax基础一
AJAX AJAX = Asynchronous JavaScript and XML(异步的 JavaScript 和 XML). AJAX 不是新的编程语言,而是一种使用现有标准的新方法. AJA ...
- java中数组的相关知识
1. 2.数组的命名方法 1)int[]ages=new int[5]; 2) int[]ages; ages=new int[5]; 3)int[]ags={1,2,3,4,5}; 4)int[ ...
- virtualbox下面安装ubuntu后外网如何远程ssh访问
这两天在折腾virtualbox安装linux的事情,想多弄几个节点,装hadoop, 环境如下 两台thinkpad, 一台正常上班用的,win7 一台装的ubuntu kylin 16.04, 上 ...
- 站点图标favicon.ico
favicon.ico图标: 网站的favicon.ico需要一次额外的http请求,无论你是否有在html里面添加 link链接 <link rel="shortcut icon&q ...
- iOS App打包上架的流程
一.申请苹果开发者账号 首先需要申请苹果开发者账号才能在APP store 里发布应用. 开发者账号分为:(1)个人开发者账号 (2)企业开发者账号 主要的区别是:点击打开链接 1.个人开发者 ...
- Android修改Eclipse 中的Default debug keystore路径,以及修改android的AVD默认路径
初学android,光是配置Eclipse就走了不少弯路,班里面有很多同学的计算 机名都是写的自己的中文姓名,结果导致了AVD文件默认保存在“C:\user\<username>\.and ...