《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. linux下svn常用命令

    (如果是第一次提交文件,很可能会出现“svn:'.'不是工作副本”,即当前目录不是工作副本,这个时候需要用到import: eg:svn import . url) 1.将文件checkout到本地目 ...

  2. QQ浏览器安卓5.8版本的Uint8Array API有bug

    调用new Uint8Array()时QQ浏览器将直接返回参数列表, 比如new Uint8Array(a)将返回[a],比如new Uint8Array(a, b)将返回[a, b],比如new U ...

  3. PD4F将HTML转换为PDF乱码问题

    修改pd4fonts.properties文件如下: KaiTi_GB2312=SIMKAI.TTFSimSun=SIMKAI.TTF

  4. Head First 设计模式读书笔记

    在网上学习了一段时间设计模式,总感觉不系统,很容易忘,最近买书,学习了<Head First设计模式>,受益匪浅,特做此记录,以便激励自己不断的向后学习. 原书JAVA版本,本次学习记录及 ...

  5. QT 第三方串口库COM10以上无法读取问题

    当COM口的端口号高于9时,也就是说从COM10往后,serialPortName赋值就不能跟COM1~~COM9一样,C++中COM大于9时,COM前面需要加上\\.\COM. 如,serialPo ...

  6. 20151012 C# 第一篇 字符与字符串

    20151012 字符与字符串: Char.String等类来表示 字符类Char 1. 字符类Char 表示一个 Unicode 字符,(Unicode字符是计算机通用的字符编码,对不同语言中的每个 ...

  7. AngularJS是什么

    先标明来源: https://code.angularjs.org/1.3.15/docs/guide/introduction 也就是官网针对1.3.15版的说明 What Is Angular? ...

  8. hdu acm 1425 sort(哈希表思想)

    sort Time Limit: 6000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submis ...

  9. 个人博客作业week2——代码复审

    1.代码规范 这些规范都是官僚制度下产生的浪费大家的编程时间.影响人们开发效率, 浪费时间的东西. 代码规范并不是从官僚制度下产生,它是为了提高项目团队开发效率而产生的一种工具,能够极大的增强代码可读 ...

  10. PYTHON学习之路_PYTHON基础(10)

    学习内容: Python进程与线程 1.线程及线程类 2.线程守护 3.线程等待 4.线程锁 5.信号量 6.timer用法 7.队列 8.事件驱动 9.生产者消费者模型 10.进程及进程同步 11. ...