为什么我必须在C var_args
函数中使用int
类型变量?就像printf
是一个内置函数,在输入各种变量之前不需要声明的参数长度...
C标准不需要具有变量参数列表的函数以具有任何特定的参数类型。
但是,对va_arg
进行适当的调用符合该功能。为此,该函数必须有某种方式知道已经通过了哪些论点。您可以以任何方式实施该要求,包括:
- 使用包含指示参数类型的转换说明符的格式字符串,就像
printf
一样。
。 - 将每个参数的数字和类型的任何描述传递。将它们全部编码为一个数组,传递几个描述其余的参数,描述一个参数,然后具有该参数,然后描述下一个参数,然后具有该参数,依此类推。
- 从操作中推断出函数执行的参数。例如,
open
呼叫可能会采用标志,以指示它应该创建文件还是仅打开现有文件。如果要创建一个文件,则可以采用一个附加参数,指示要在文件上设置的权限,而如果它只是打开现有文件,则不会使用该参数。
答案与编译器调用约定有关。这是一个简化的解释:
基本上,当编译器与n个参数遇到函数调用时,它会生成代码以在堆栈上推出n个参数。
当编译器编译一个参数的函数时,它会生成代码以在堆栈中弹出n个参数。
没有"秘密"信息告诉函数,推出了多少个参数。对于普通函数,参数的数量和类型仅是因为调用代码中的函数原型与函数定义一致。
对于variadic函数,不可能依靠函数定义。...
的字面意思是"我们不知道会推出哪些参数。因此,需要有一个替代来源来进行参数规范。一种简单的方法就是确保第一个参数是固定尺寸的参数计数跟随。
printf
和家庭使用其他方法。格式字符串定义了将使用的类型和数量。
c不允许变量参数函数可选为零参数。也就是说,在...
之前必须有一个命名参数。
对于printf
,命名参数是格式字符串。解析格式字符串时,可以推导下一个参数的类型,并且可以正确调用va_arg
宏。
如果所有参数均为相同的类型,则可以使用命名参数指示所传递的参数数。例如,如果您通过许多字符串传递:
void foo_strings (int n, ...) {
va_list ap;
va_start(ap, n);
while (n--) {
const char *s = va_arg(ap, const char *);
foo(s);
}
va_end(ap);
}
但是,您可以很容易地使用NULL
表示没有更多的字符串。然后,第一个参数可以用于其他内容,例如指向函数。
void foo_string (void (*foo)(const char *), ...) {
va_list ap;
va_start(ap, n);
const char *s = va_arg(ap, const char *);
while (s != NULL) {
foo(s);
s = va_arg(ap, const char *);
}
va_end(ap);
}