可变长度参数



为什么我必须在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);
}

相关内容

  • 没有找到相关文章

最新更新