C/C++可变参数列表参数处理(va_list、va_start()、va_copy()、va_arg()、va_end())

文章目录

  • C/C++可变参数列表参数处理(va_list、va_start()、va_copy()、va_arg()、va_end())
    • 1 引言
    • 2 类型、函数介绍
      • 2.1 va_list
      • 2.2 va_start()
      • 2.3 va_copy()
      • 2.4 va_arg()
      • 2.5 va_end()
    • 3 示例
    • 4 总结

1 引言

  在学习 C/C++ 的过程中经常会看到一些函数,使用的是可变参数列表,函数在定义是,没有规定参数个数与参数类型,而是使用 ... 代替。如果是第一次见到这种表达方式吗,可能会感到很神奇。这里的 ... 就代表了可变参数列表部分。
  这种方式允许函数接受类型和数目不确定的一些列参数,那么函数内部是如何对可变的参数列表进行处理的呢?这里对可变参数列表的处理,就需要用到 va_listva_startva_copyva_argva_end 。接下来分别对它们进行介绍。

2 类型、函数介绍

2.1 va_list

  对应的定义如下:

 typedef /* unspecified */ va_list;  /* 未指定具体类型 */

  这里的 va_list 是一个完整对象类型(complete object type),用来保存 va_start, va_copy, va_arg, va_end 这些函数需要的数据。
  如果创建了一个 va_list 实例,它被传递给另一个函数,并且在该函数中通过 va_arg() 使用,那么用在调用函数中的任意后续参数都应该在调用 va_end() 之前被处理。
  可以把指向 va_list 类型的指针传递给另一个函数,然后在函数返回后使用该对象,也就是说,可以使用指针访问该类型对象。

2.2 va_start()

  函数对应的定义如下:

 void va_start( std::va_list ap, parm_n );

  这个宏定义能够使 va_list 类型的对象能够访问可变参数列表,使 va_list 对象指向可变参数列表可变部分的前一个参数(也就是最后一个固定的参数,说可变部分,是因为整个括号内的函数的参数一起才称之为可变参数列表,而前边的参数往往是固定的,可变部分在整个参数列表的后边)。这实际上是一个初始化的过程,我们可以理解为,这个函数告知 va_list 类型的对象从哪个参数开始是参数列表中可变的部分,让它知道从哪里开始工作,便于后续进行逐个访问使用。
  这里的 parm_n 通常是可变参数 ... 前边的一个参数,这里的 ap 经过 va_start() 函数初始化之后,指向 ... 部分对应的第一个参数。
  va_start() 应该在任意的 va_arg() 调用前被 va_list 类型的对象调用,注意这里不是必须要调用 va_start() 函数,还可以使用 va_copy() ,后续会进行介绍,两个宏函数根据需要在调用 va_arg() 函数前有调用即可。

2.3 va_copy()

  函数对应的定义如下:

 void va_copy( std::va_list dest, std::va_list src ); // C++11、C99引入

  结合函数定义,很容易可以理解,这是一个将已有的 va_list 类型对象内容拷贝到另一个 va_list 类型对象的过程,这个过程与调用 va_start() 函数进行初始化由相同的作用。
  va_end() 应该在函数返回之前或者任意子序列重新被初始化之前被 dest 调用。这里也不难理解,函数返回之前,即 va_list 对象已经完成了本次的“任务”,需要清理一下;dest 中的部分参数重新初始化之前也是如此,也需要调用 va_end() 函数进行清理工作。

2.4 va_arg()

  函数对应的定义如下:

 T va_arg( std::va_list ap, T );

  va_arg() 宏函数根据 va_list 类型对象的下一个参数展开为类型 T 的表达式,移动指针,将 va_list 对象的指针指向可变参数列表中的下一个参数。
  在调用 va_arg 之前,va_list 类型的对象必须经过 va_startva_copy() 的初始化,且不能有调用 va_end(),每次调用 va_arg() 会将 va_list 对象的指针指向可变参数列表中的下一个参数。
  如果可变参数列表中的下一个参数与类型 T 不兼容,行为未定义,除非:

  • 一种类型是有符号整型,另一种类型是相应的无符号整型,值在两种类型中都可以表示;
  • 一种类型是指向void的指针,另一种类型是指向字符类型(char、signed char或unsigned char)的指针。
      如果va_arg() 调用的时候 va_list 类型对象(也称为实例)中已经没有参数了,对应的行为也没有定义。
2.5 va_end()

  函数对应的定义如下:

 void va_end( std::va_list ap );

  va_end() 对由 va_start() 或者 va_copy() 初始化的 va_list 类型对象的执行清理过程。va_end() 会修改 va_list 对象实例,使其不再可用。
  如果没有相对应的对 va_start() 或者 va_copy() 函数的调用,或者如果在一个函数调用 va_start() 或者 va_copy() 函数之前没有调用 va_end() ,行为没有定义,即在这些情况下,没有定义如何去处理。

3 示例

#include <iostream>
#include <cstdarg>int add_nums(int count, ...)
{int result = 0;std::va_list args;                     /* 创建一个 va_list 实例 */va_start(args, count);                 /* 对实例进行初始化,这里的count是可变参数列表的第一个参数,而调用va_start()函数之后,args实际指向count */for (int i = 0; i < count; ++i) {result += va_arg(args, int);      /* 读取args指向位置的下一个参数,扩展为int类型,并移动指针到下一个位置 */}va_end(args);                         /* 清理va_list实例 */return result;
}int main()
{std::cout << add_nums(4, 25, 25, 50, 50) << '\n';
}

4 总结

  va_listva_start()va_copy()va_arg()va_end() 是可变参数列表处理过程中使用到的数据类型和处理方法, va_list 是用于处理可变参数的数据,va_start()、**va_copy()**则负责对该数据对象进行初始化,va_arg() 则负责对可变参数按照指定类型进行扩展,并移动指针,va_end() 负责对该数据进行清理工作。把这些放在一个流程中进行理解,应该回容易一些。

参考文章:

  • va_liast cppreference.com
  • 揭秘X86架构C可变参数实现原理
  • C语言,三个点…

C/C++可变参数列表参数处理方法va_list、va_start()、va_copy()、va_arg()、va_end()相关推荐

  1. java 可变参数列表_java中可变参数列表的实现方法

    我们在对可变参数有一定的认识后,可以引申一下它的使用范围.在数组中也会需要参数的传入,那么结合参数的数量不固定,我们在参数类型上也得到了增加,这就是本篇所要讲的可变参数列表.下面我们就java可变参数 ...

  2. c语言中的函数可变参数列表相关的三个宏

    在stdarg.h头文件中声明了一个类型va_list和3个与函数可变参数列表有关的宏:va_start.va_arg.va_end. #include<stdarg.h> //包含宏相关 ...

  3. php函数可变参数列表,PHP函数可变参数列表的具体实现方法介绍

    也许对于PHP初级程序员来说,对于PHP函数并不能完全熟练的掌握.我们今天为大家介绍的PHP函数可变参数列表的实现方法主要是利用func_get_args(). func_num_args().fun ...

  4. 理解可变参数va_list、va_start、va_arg、va_end原理及使用方法

     参考: http://www.360doc.com/content/12/0309/10/4025635_192940551.shtml http://www.cnblogs.com/Annie ...

  5. c语言全局变量作为参数_在C / C ++中使用变量参数列表

    c语言全局变量作为参数 C/C++ provides a means to pass a variable number of arguments to a function.  This artic ...

  6. 【Kotlin】函数类型 ( 函数类型 | 带参数名称的参数列表 | 可空函数类型 | 复杂函数类型 | 带接收者函数类型 | 函数类型别名 | 函数类型实例化 | 函数调用 )

    文章目录 I . 函数类型 II . 带参数名的参数列表 III . 可空函数类型 IV . 复杂函数类型解读 V . 函数类型别名 VI . 带 接收者类型 的函数类型 VII . 函数类型实例化 ...

  7. 鱼眼参数的数值计算优化方法

    在下面两篇文章中,我们大概介绍了开源图像处理软件 Hugin 中鱼眼图像的矫正方法,即如何将目标全景图上的坐标映射到鱼眼图像上,从而获取相应的像素信息. https://blog.csdn.net/q ...

  8. 可变参数:va_list(),va_start(),va_arg(),va_end() 详细解析

    目录 1.含义: 2.使用: 3.连续打印出自定义格式的文字: 1.含义: (1)va_list是C语言中的一个宏定义,用于表示一个变长参数列表.它是一个指向变长参数列表的指针,可以通过宏va_sta ...

  9. 如何获取函数的变长参数(va_list, va_start, va_arg, va_end)

    最近在花时间研读C++. 函数这章讲到了函数的变长参数(ellipsis...),但是primer中讲得比较浅,提到了怎么声明怎么调用,但是没有写明在函数内部是如何获取变长的参数的. 1)省略号(el ...

  10. 【SSM - SpringMVC篇】03 - SpringMVC的参数绑定 - 参数自动绑定 - javabean对象参数绑 - 嵌套bean参数绑定

    文章目录 SpringMVC的参数绑定 1 传统方式进行参数传递 2 通过SpringMVC的参数绑定实现[***用这个就行] 2.1 SpringMVC的基本数据类型的参数绑定 2.1.1 方法形式 ...

最新文章

  1. Android 5.0新特性之沉浸式状态栏
  2. SQL SERVER全面优化-------写出好语句是习惯
  3. visual C++如何查看汇编代码
  4. c2665 “initgraph”: 2 个重载中没有一个可以转换所有参数类型_Python 命令行之旅:深入 click 之参数篇...
  5. HTTP,FTP,TCP,UDP及SOCKET
  6. 关于批量插入数据之我见(100万级别的数据,mysql) (转)
  7. 1.13 南京站 | 2022 开年 Serverless 沉浸式技术实践营开始报名
  8. php在sql中添加数据,使用php在oracle数据库中插入数据
  9. 0点mysql_【转载】MySQL查询当天0点,昨天时间
  10. 使用代码创建具有organization unit的opportunity
  11. Toolbar设置详解----掉坑总结
  12. IPv6 to IPv4过渡技术——6to4隧道配置实例
  13. c语言浪漫烟花表白,C语言实战之浪漫烟花表白程序.pdf
  14. [整理][VBA]Excel合并表格
  15. Rasa课程、Rasa培训、Rasa面试、Rasa实战系列之Understanding Word Embeddings 1_ Just Letters
  16. 跟小博老师一起学习数据库 ——MySql安装
  17. Raspberry Pi 3B学习笔记
  18. 刘可-寂寞才说爱 试听+下载+歌词
  19. 异常-异常场景的测试
  20. 20155337 《网络安全编程》实验五实验报告

热门文章

  1. java编程题身高排队,试题 算法训练 预测身高
  2. synchronized和ReentrantLock性能分析
  3. 微信小程序动态更改标题栏_微信小程序动态改变导航栏标题和背景颜色
  4. 局域网即时通讯Active Messenger 完美破解版本 最新版本破解
  5. 单工,半双工和和全双工通讯的概念
  6. pycharm前进、后退快捷键
  7. SU-03T语音模块的使用(小智语音控制LED灯)
  8. Win10系统开启黑暗主题
  9. python 语音处理工具包AudioSegment的基本使用
  10. Summation Prove (Intro. to Algo. Open Course Episode 5) in Quick Sort