在C语言编程过程中,常规函数的形式参数数量固定,调用时需提供与形式参数一一对应的实参。然而,有时我们希望函数能根据需求接受不同数量的参数,如常见的printf()
、scanf()
函数以及系统调用execl()
等。这类函数如何实现呢?C编译器通过一系列宏(如va_start
、va_arg
、va_end
)来支持这一特性,这些宏有助于跨平台编程,增强代码的可移植性。
宏的使用
宏的两种形式
-
ANSI标准形式:至少包含一个普通形式参数,之后跟随省略号
...
作为函数原型的一部分。type funcname(type para1, type para2, ...)
-
UNIX System V兼容形式:无需普通形式参数,使用
va_alist
和va_dcl
宏定义。type funcname(va_alist) va_dcl
其中,va_dcl
是对va_alist
的进一步声明,依平台而异,但都以分号结束,因此在声明后不再添加分号。推荐使用符合ANSI标准的<stdarg.h>
头文件,因为它具有更好的跨平台兼容性,而<varargs.h>
主要用于保持与旧代码的兼容。
宏功能简介
va_start(argp, last_fixed_arg)
:初始化argp
指针,使其指向第一个可变参数。va_arg(argp, type)
:返回参数列表中的当前参数,并将argp
移动到下一个参数位置。va_end(argp)
:清理argp
指针,通常设置为NULL
。
调用者需以特定方式指示可变参数的数量,如以特定值(如空字符串、-1
)结束参数列表,或像printf()
那样通过第一个参数来确定参数个数。
示例代码
ANSI标准形式示例
#include <stdio.h>
#include <stdarg.h>
// 原型声明,至少一个固定参数后跟...
int demo(char *msg, ...);
int main(void) {
demo("DEMO", "This", "is", "a", "demo!", "\0");
return 0;
}
// 函数定义
int demo(char *msg, ...) {
va_list argp;
int argno = 0;
char *para;
va_start(argp, msg);
while (1) {
para = va_arg(argp, char *);
if (!strcmp(para, "\0")) break;
printf("Parameter #%d is: %s\n", argno, para);
argno++;
}
va_end(argp);
return 0;
}
UNIX System V兼容形式示例
#include <stdio.h>
#include <varargs.h>
// 注意:此示例在不同平台上可能需要调整,如去除va_list或修改声明方式
int demo(va_alist);
va_dcl
int main(void) {
demo("This", "is", "a", "demo!", "\0");
return 0;
}
int demo(va_alist) va_dcl {
va_list argp;
int argno = 0;
char *para;
va_start(argp);
while (1) {
para = va_arg(argp, char *);
if (!strcmp(para, "\0")) break;
printf("Parameter #%d is: %s\n", argno, para);
argno++;
}
va_end(argp);
return 0;
}