C语言可变长的参数列表详解

C语言可变长的参数列表

C语言可创建接收参数个数不确定的函数。如常用的标准库函数printf就是一个接收参数个数可变的函数。函数printf至少要接收一个字符串作为它的第一个实参。但事实上,printf还能够接收任意数目的其他实参。printf的函数原型是:

int printf(const char *format, ...);

其中的省略号(…)表示这个函数可以接收可变数目的各种类型的实参。

需要注意:这个省略号必须放在形参列表的末尾。

可变参数头文件<stdarg.h>中的宏和定义,为创建一个可变长参数列表的函数提供了必须的功能。

stdarg.h可变长参数列表类型和宏:

标识符 说明
va_list 该类型适合于保存宏va_start、va_arg、和va_end所需的信息。为了访问到一个可变长参数列表中的参数,必须定义一个类型为va_list的对象
va_start 在一个可变长参数列表中的参数被访问前,先调用这个宏。这个宏将初始化用va_list声明的对象,以供va_arg和va_end使用
va_arg 这个宏展开成一个表示可变长参数列表中下一个参数的值的表达式,值的类型由宏的第二个参数决定。每次对va_arg的调用都要修改用va_list声明的对象,以使这个对象指向列表中的下一个实参
va_end 当一个函数的可变长实参列表是通过宏va_start来引用时,宏va_end可用于从这样的函数中正常返回

C11增加了宏va_copy,这个宏接受两个va_list并将第二个参数复制给第一个参数。这就允许通过可变长参数列表的多次传递不用每次都从头开始。
下面演示一个接收可变个数实参的函数average。函数average的第一个实参总是待计算平均值的数据的个数。

#include<stdio.h>
#include<stdarg.h> 

double average(int i, ...) {
	double total = 0;
	va_list ap;
	va_start(ap, i);

	for (int j = 1; j <= i; ++j) {
		total += va_arg(ap, double);
	}

	va_end(ap);
	return total / i;
}

int main() {
	double w = 37.5;
	double x = 22.5;
	double y = 1.7;
	double z = 10.2;

	printf("w = %.lf\nx = %.lf\ny = %.lf\nz = %.lf\n", w, x, y, z);
	printf("w和x的平均数:%.3f\nw、x和y的平均数:%.3f\nw、x和y和z的平均数:%.3f\n", average(2, w, x), average(3, w, x, y), average(4, w, x, y, z));
}

除了宏va_copy,函数average使用了头文件<stdarg.h>中所有的定义和宏。

上面的代码中,va_list类型的对象ap被宏va_start、va_arg和va_end用来处理函数average的可变长参数列表。

函数首先调用宏va_start来初始化对象ap,为宏va_arg和va_end使用ap做准备。

宏va_start接收两个实参——对象ap和参数列表中在省略号前的最右边的标识符,在本例中是i,宏va_start在这里使用i来判断可变长实参列表从哪里开始。

然后函数average反复地将可变长参数列表中的参数加到变量total上。通过宏va_arge,参数列表中的数据不断地被提取出来加到变量total上。

宏va_arg接收两个实参——对象ap和期望在实参列表中出现的数据的类型,在本例中是double。宏va_arg返回的是实参的值。

函数average用对象ap作为一个实参来调用宏va_end以实现从函数average正常返回到函数main中。

像printf和scanf这样带可变参数列表的函数是如何知道每个va_arg宏使用的是何种类型的数据呢?

在程序指向过程中,通过扫描格式控制字符串中的格式转换说明符来确定下一个将要出来的实参是何种类型。

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注我们的更多内容!

(0)

相关推荐

  • 解析Java的可变长参数列表及其使用时的注意点

    Java 可变参数列表 复制代码 代码如下: class A {} 由于所有的类都继承于Object,可以以Object数组为参数的方法: public class parameter { static void printArray(Object[] args){ for(Object obj : args){ System.out.print(obj + " "); } System.out.println(); } public static void main(String[]

  • Java可变参数列表详解

    Java可变参数列表详解 1.接受的传入参数情况: 如public void test(String ...args){...} 1)不使用参数,如test() 2)使用一个或多个参数,如test("1"); test("1","2"); 3)使用数组 test(new String[]{"1","2"}); 2.方法内部访问参数: 在test方法内部,我们可以像使用数组的访问方式一样来访问参数args.如

  • Scala可变参数列表,命名参数和参数缺省详解

    重复参数 Scala在定义函数时允许指定最后一个参数可以重复(变长参数),从而允许函数调用者使用变长参数列表来调用该函数,Scala中使用"*"来指明该参数为重复参数.例如: scala> def echo (args: String *) = | for (arg <- args) println(arg) echo: (args: String*)Unit scala> echo() scala> echo ("One") One sca

  • C语言可变长的参数列表详解

    C语言可变长的参数列表 C语言可创建接收参数个数不确定的函数.如常用的标准库函数printf就是一个接收参数个数可变的函数.函数printf至少要接收一个字符串作为它的第一个实参.但事实上,printf还能够接收任意数目的其他实参.printf的函数原型是: int printf(const char *format, ...); 其中的省略号(…)表示这个函数可以接收可变数目的各种类型的实参. 需要注意:这个省略号必须放在形参列表的末尾. 可变参数头文件<stdarg.h>中的宏和定义,为创

  • C语言main()函数的参数问题详解

    #include<stdio.h> void main(int argc, char* argv[]) { while (argc > 1) { ++argv; printf("%s\n", argv); --argc; } } #include<stdio.h> #include<stdlib.h> void main(int argc, char* argv[]) { int i; printf("The number of st

  • C++11新特性之变长参数模板详解

    目录 C++11 变长参数模板 变长函数参数包 如何解参数包 sizeof()获得函数参数个数 递归模板函数 变参模板展开 结论 C++11 变长参数模板 在C++11之前,无论是类模板 还是函数模板,都只能按其指定的样子,接受一组固定数量的模板参数: 这已经大大提升了代码的复用! 在C++11之后,加入了新的表示方 法,允许任意个数.任意类别的模板参数,同时也不需要在定义时将参数的个数固定.更加像"黑魔法"了. template<typename... Ts> class

  • C语言数据输入与输出实例详解

    C语言数据输入与输出实例详解 1 概论 C语言提供了跨平台的数据输入输出函数scanf()和printf()函数,它们可以按照指定的格式来解析常见的数据类型,例如整数,浮点数,字符和字符串等等.数据输入的来源可以是文件,控制台以及网络,而输出的终端可以是控制台,文件甚至是网页. 2 数据输出 从第一个c语言程序中,就使用了跨平台的库函数printf实现将一段文字输出到控制台,而实际上,printf()不仅可以将数据按照指定的格式输出到控制台,还可以是网页或者是指定的文件中,printf()函数执

  • Go语言并发编程基础上下文概念详解

    目录 前言 1 Go 中的 Context 2 Context 接口 3 Context Tree 4 创建上下文 4.1 上下文创建函数 4.2 Context 使用规范 4.3 Context 使用场景 5 总结 前言 相信大家以前在做阅读理解的时候,一定有从老师那里学一个技巧或者从参考答案看个:结合上下文.根据上下文我们能够找到有助于解题的相关信息,也能更加了解段落的思想. 在开发过程中,也有这个上下文(Context)的概念,而且上下文也必不可少,缺少上下文,就不能获取完整的程序信息.那

  • Java语言中的内存泄露代码详解

    Java的一个重要特性就是通过垃圾收集器(GC)自动管理内存的回收,而不需要程序员自己来释放内存.理论上Java中所有不会再被利用的对象所占用的内存,都可以被GC回收,但是Java也存在内存泄露,但它的表现与C++不同. JAVA中的内存管理 要了解Java中的内存泄露,首先就得知道Java中的内存是如何管理的. 在Java程序中,我们通常使用new为对象分配内存,而这些内存空间都在堆(Heap)上. 下面看一个示例: public class Simple { public static vo

  • C语言中的正则表达式使用示例详解

    正则表达式,又称正规表示法.常规表示法(英语:Regular Expression,在代码中常简写为regex.regexp或RE).正则表达式是使用单个字符串来描述.匹配一系列符合某个句法规则的字符串. 在c语言中,用regcomp.regexec.regfree 和regerror处理正则表达式.处理正则表达式分三步: 编译正则表达式,regcomp: 匹配正则表达式,regexec: 释放正则表达式,regfree. 函数原型 /* 函数说明:Regcomp将正则表达式字符串regex编译

  • Java中可变长度参数代码详解

    到J2SE1.4为止,一直无法在Java程序里定义实参个数可变的方法--因为Java要求实参(Arguments)和形参(Parameters)的数量和类型都必须逐一匹配,而形参的数目是在定义方法时就已经固定下来了.尽管可以通过重载机制,为同一个方法提供带有不同数量的形参的版本,但是这仍然不能达到让实参数量任意变化的目的. 然而,有些方法的语义要求它们必须能接受个数可变的实参--例如著名的main方法,就需要能接受所有的命令行参数为实参,而命令行参数的数目,事先根本无法确定下来. 对于这个问题,

  • python3中sorted函数里cmp参数改变详解

    今天在刷leetcode的时候,对于179题返回最大数,用python2中的sorted(cmp)会很方便,但是在python3中这一参数被取消了,经过查找,发现应该借助functools中的cmp_to_key函数,直接贴代码 import functools def cmp(a,b): if a > b : return -1 elif a < b : return 1 else: return 0 nums = [1,2,3,4,5,6] sorted_nums = sorted(num

随机推荐