《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的用法的更多相关文章

  1. C语言变长参数实现

    #include<stdio.h> #include<string.h> #include<stdarg.h> /***编写可变长参数列表的函数案例*/ /* vo ...

  2. 介绍C++11标准的变长参数模板

    目前大部分主流编译器的最新版本均支持了C++11标准(官方名为ISO/IEC14882:2011)大部分的语法特性,其中比较难理解的新语法特性可能要属变长参数模板(variadic template) ...

  3. C++中的变长参数

    新参与的项目中,为了使用共享内存和自定义内存池,我们自己定义了MemNew函数,且在函数内部对于非pod类型自动执行构造函数.在需要的地方调用自定义的MemNew函数.这样就带来一个问题,使用stl的 ...

  4. Scala 变长参数

    如果Scala定义变长参数 def sum(i Int*), 那么调用sum时,可以直接输入sum(1,2,3,4,5) 但是不可以sum(1 to 5) 必须要将1 to 5 强制为seq sum( ...

  5. C++11变长参数模板

    [C++11变长参数模板] C++03只有固定模板参数.C++11 加入新的表示法,允许任意个数.任意类别的模板参数,不必在定义时将参数的个数固定. 实参的个数也可以是 0,所以 tuple<& ...

  6. 【Unix环境高级编程】编写变长参数函数

    文件的格式输入输出函数都支持变长参数.定义时,变长参数列表通过省略号'...'表示, 因此函数定义格式为: type 函数名(parm1, parm2,parmN,...); Unix的变长参数通过v ...

  7. java常量和变量的定义规则,变长参数的使用

    首先是定义的一般规则,类名首字母全部大写,常量全部大写用下划线分隔,变量用驼峰形式.注意使用long赋值用L时不能写小写的L要写大写的,不然会和数字“1”傻傻分不清. 下面是举例: public cl ...

  8. Lua 变长参数(variable number of arguments)

    lua变长参数 function add ( ... ) for i, v in ipairs{...} do print(i, ' ', v) end end add(1, 2, 'sdf') lu ...

  9. 【小白学Lua】之Lua变长参数和unpack函数

    一.简介 Lua的变长参数和unpack函数在实际的开发中应用的还挺多的,比如在设计print函数的时候,需要支持对多个变量进行打印输出,这时我们就需要用到Lua中的变长参数和unpack函数了. 二 ...

随机推荐

  1. cookie session URL重写 与考试

    状态管理.Cookie.Session.URL重写 HTTP协议:无状态的连接(每次连接都是新的请求)1.隐藏字段 <input type="hidden" name=&qu ...

  2. CSS 部件

    1.导航菜单: [荐]抽屉式菜单 jQuery.mmenu jQuery.mmenu 实现了类似手机上经常使用的抽屉式菜单,效果很好.http://mmenu.frebsite.nl/ 2.jQuer ...

  3. 使用Concurrency Visualizer优化性能

    Concurrency Visualizer: https://msdn.microsoft.com/en-us/library/dd537632.aspx?f=255&MSPPError=- ...

  4. Android视图绘制流程完全解析,带你一步步深入了解View(二)

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/16330267 在上一篇文章中,我带着大家一起剖析了一下LayoutInflater ...

  5. flask_关注者,联系人和好友

    在这节我们实现的功能比较复杂,就是实现用户"关注"和"取消关注"的功能. 一个用户可以关注多个其他的用户,一个用户也可以被其他多个用户所关注,这样看的话,在数据 ...

  6. C#基于Office组件操作Excel

    1.    内容简介 实现C#与Excel文件的交互操作,实现以下功能: a)     DataTable 导出到 Excel文件 b)     Model数据实体导出到 Excel文件[List&l ...

  7. 【NHibernate】列“ReservedWord”不属于表 ReservedWords

    NHibernate+FluentNHibernate+MySql 运行时黄页显示下边的异常,项目中找了半天没出现过这个列的关键字. [ArgumentException: 列“ReservedWor ...

  8. Python socket (单线程)

    client, 客户端 code : 客户端主要方法, s.send(); s.sendall(); s.recv(); s.connect() class Client(object): def _ ...

  9. Windows Phone 8.1 新特性 - 控件之FlipView

    本篇为大家介绍 Windows Phone 8.1 中新增的 FlipView 控件,它的中文名字叫做:翻转视图. 虽然听起来有点拗口,但是它的用途大家一定不会陌生.在 Windows Phone 8 ...

  10. Android最大可运行内存

    int maxMemory = (int) Runtime.getRuntime().maxMemory();