700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > C/C++可变参数列表参数处理方法va_list va_start() va_copy() va_arg() va_end()

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

时间:2024-01-15 03:28:03

相关推荐

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

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_list2.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 揭秘X86架构C可变参数实现原理C语言,三个点…

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。