C 语言的可变参数

可变参数函式

在C中函式int printf(const char *format, ...)就是用来输出讯息到画面上的,在这个函式的参数中有个比较特别的参数...,它就是C语言用来表达可变参数,意即参数的数量不定。

#include<stdio.h>#include<stdarg.h>void printList(int head, ...){    va_list args;    va_start(args, head);        for (int i = head; i != -1; i = va_arg(args, int)){        printf("%d\n", i);    }        va_end(args);}int main(void){    printList(4, 5, 6, 7, 8, 9, -1);}

输出结果

456789

上面的程式码有几个我们需要关注的点。

va_list: 型态是指标,可能是void*char*,依据不同编译器,可能会有不同结果。va_start: 是一个marco,样式void va_start(va_list arg_ptr, last_arg)va_arg: 是一个marco,样式type va_arg(va_list arg_ptr, type)va_end: 是一个marco,样式void va_end(va_list arg_ptr)

以上这些都是包含在C的<stdarg.h>中。
在函式的参数以...代替一般参数,之后再调用函式时就可以填入任意数量的参数。

arg_ptr是一个变数,用来指向最后一个固定的参数last_arg(即範例中的head)。
va_start用来抓取最后一位固定的参数,使arg_ptr指向last_arg的下一个参数,即第一个可变参数。
va_arg则是用来抓取那些任意数量的参数,type指的是传入参数的资料型态,由于取值的方法跟地址偏移量有关,所以需要知道资料类型。
va_end用来表示终止检索那些传入任意参数,实际操作只是使arg_ptr指向0以免意外发生。

这里有几个重点

宣告的固定参数在调用时不能少之后任意数量的参数型态都要一样(即...所包含的参数型态都要一样)macrova_arg抓参数时,没法辨认有多少个参数,不知何时终止,需要程式设计师自己处理。参数的型态有许多限制,大多数都不能用,或者会被强制转成int unsigned int double,下面这些不能使用。char、signed char、unsigned char、short、unsigned short、signed short、floatC99标準添加了va_copy

这里提一个我觉得比较重要的一点,我们可能会有个疑惑,为啥C设计时要我们设计师自己处理参数的数量,不能在传入时顺便统计一下。
问题点就是va_arg这个marco取得参数的设计,由于函式在堆叠区展开时参数的地址是连续的,va_arg抓取参数时是由指标移动偏移量取得的,这样就没法得知我们传入了多少参数。

我们较常处理这问题的方法:

传入一个数字,告知函式有多少个参数需要处理传入一个终止符,在检索到终止符时自动停止

在C的<stdarg.h>中,还有个marco_INTSIZEOF(n)跟上述的那些marco的运作和记忆体对齐有关,有兴趣了解的可以去翻翻源码。


可变参数marco

我们在定义marco也可以像函式使用...,表达可变参数。

#include<stdio.h>// __VA_ARGS__是原本C用来表达可变参数的替代词#define feedback1(...) ( \            printf(__VA_ARGS__) \        )// 如果我们不想用C定义的__VA_ARGS__// 我们可以自己定义一个名称,格式如下#define feedback2(args...) ( \            printf(args) \        )// 下面的例子相比上面多个##// 如果我们没有传入参数给args// 那么按照define的功能,文字替代// 文字替代后会产生printf(format, )// 这样因为有','所以编译会有问题// ##有个功能就是在没有参数传入的情况下// 消掉多余的',',使得结果变成printf(format)#define my_print(format, args...) ( \            printf(format, ##args) \        )int main(void) {    feedback1("feedback1: I'm hungry.\n");    feedback2("feedback2: I'm hungry.\n");    my_print("my_print: I'm hungry.\n");    my_print("my_print: num1 = %d, num2 = %d\n", 45, 54);    return 0;}

输出结果

feedback1: I'm hungry.feedback2: I'm hungry.my_print: I'm hungry.my_print: num1 = 45, num2 = 54

feedback1__VA_AGRS__是C自有的名称,编译器会用来换成可变参数。
但我们也可以自己定义名称,格式如feedback2args...
当没有额外参数传入可能会产生多余的,,造成格式编译不过。
##可以在没有参数的情况下,消掉多余的,
marco的可变参数,没办法单独读取参数,意即我们没办法像函式va_arg一次次抓取变数,所以相对于函式有时候并没法简单值观的实现某些特定方法。


关于作者: 网站小编

码农网专注IT技术教程资源分享平台,学习资源下载网站,58码农网包含计算机技术、网站程序源码下载、编程技术论坛、互联网资源下载等产品服务,提供原创、优质、完整内容的专业码农交流分享平台。

热门文章