未指定C函数中的参数数-void foo()

未指定C函数中的参数数-void foo(),c,function,arguments,parameter-passing,variadic-functions,C,Function,Arguments,Parameter Passing,Variadic Functions,我读到,在Cvoid foo()中,它意味着一个函数foo接受了数量不定、类型不定的参数 有没有人能给我举个例子,其中一个C函数接受的参数数目不确定?这在C中可以应用于什么?我在网上找不到任何东西。这是一个老式的函数声明 本声明: void foo(); 声明foo是一个返回void的函数,该函数接受未指定但固定数量和类型的参数。这并不意味着带有任意参数的调用是有效的;这意味着编译器无法诊断具有错误数量或类型参数的错误调用 在某个地方,可能在另一个翻译单元(源文件)中,必须有函数的定义,可能:

我读到,在C
void foo()
中,它意味着
一个函数foo接受了数量不定、类型不定的参数

有没有人能给我举个例子,其中一个C函数接受的参数数目不确定?这在C中可以应用于什么?我在网上找不到任何东西。

这是一个老式的函数声明

本声明:

void foo();
声明
foo
是一个返回
void
的函数,该函数接受未指定但固定数量和类型的参数。这并不意味着带有任意参数的调用是有效的;这意味着编译器无法诊断具有错误数量或类型参数的错误调用

在某个地方,可能在另一个翻译单元(源文件)中,必须有函数的定义,可能:

void foo(x, y)
long x;
double *y;
{
    /* ... */
}
这意味着对
foo
的任何调用,如果没有传递两个类型为
long
double*
的参数,则该调用无效,并且具有未定义的行为

在1989年ANSI C标准之前,这些是语言中唯一可用的函数声明和定义,编写正确的函数调用完全由程序员承担。ANSI C增加了原型,即指定函数参数类型的函数声明,允许对函数调用进行编译时检查。(这个特性是从早期C++借用的)。
void foo(long x, double *y);

/* ... */

void foo(long x, double *y) {
    /* ... */
}
旧式(非原型)声明和定义仍然是合法的,但它们已经正式过时,这意味着,原则上,它们可以从该语言的未来版本中删除——尽管它们仍然存在于2011年的标准中,我不知道这是否真的会发生

在现代C代码中使用旧式函数声明和定义是没有充分理由的。(我在一些角落案例中看到了使用它们的理由,但我发现它们不令人信服。)


C还支持变量函数,如
printf
,它接受任意数量的参数,但这是一个独特的特性。变量函数必须用原型声明,原型包括尾部的
,…
。(调用没有可见原型的变量函数并不违法,但它有未定义的行为。)函数本身使用
中定义的宏来处理其参数。与旧式函数声明一样,没有编译时检查与
,…
相对应的参数(尽管有些编译器可能会检查某些调用;例如,如果
printf
调用中的参数与格式字符串不一致,gcc会发出警告).

这意味着您没有告诉编译器函数采用什么参数,这意味着它不会保护您使用任意参数集调用它。但是,您需要在定义中明确说明要实现的函数实际采用了哪些参数


例如,如果您正在生成一个头文件来在外部代码中描述一个外部函数,但是您不知道函数的签名是什么,然后仍然可以使用标头调用它,但如果在调用结果中提供了错误的参数,则未定义。

C函数调用标准允许使用零个或多个参数调用函数,并且参数的数量可能与函数接口匹配,也可能与函数接口不匹配

其工作方式是调用方在被调用函数返回后调整堆栈,而不是被调用函数调整堆栈,这与其他标准(如Pascal)不同,Pascal要求被调用函数正确管理堆栈调整

因为调用方知道在调用被调用函数之前哪些参数及其类型被推送到堆栈上,而被调用函数不知道,所以在被调用函数返回后,调用方需要清除堆栈中的推送参数

随着C标准的更新,函数调用接口描述变得更加复杂,以允许编译器检测并报告原始K&R C标准允许编译器未检测到的接口问题

现在的标准是,在被调用函数接口规范或声明中最后一个已知和指定的参数之后,使用三个句点或点的省略号表示法指定变量参数列表

因此,对于一些标准C库I/O函数,您将看到如下内容:

 int sprintf (char *buffer, char *format, ...);
这表明函数sprintf要求第一个参数是指向缓冲区的char指针,第二个参数是指向格式字符串的char指针,可能还有其他参数。在这种情况下,需要为格式字符串中的打印格式说明符插入任何附加参数。如果格式字符串只是一个没有指定格式的文本字符串(例如,整数为%d),则不会有其他参数

较新的C标准指定了一组用于变量参数列表的函数/宏,即varg函数。使用这些函数/宏,被调用函数可以单步遍历参数列表的变量部分并处理参数。这些函数如下所示:

int jFunc (int jj, char *form, ...)
{
   va_list myArgs;
   int     argOne;

   va_start (myArgs, form);
   argOne = va_arg (myArgs, int);
   va_end (myArgs);

   return 0;
}
变量参数列表的问题是C没有一种方式来传递变量参数,甚至没有多少个参数。因此,函数的设计者必须提供一种机制。对于C标准库I/O函数,通过为每个参数指定格式说明符,使用表示格式字符串后面的参数数量的格式完成此操作。由于没有任何东西可以进行一致性检查,因此最终可以得到一种格式
fd = open(filename, O_RDONLY);
fd = open(filename, O_CREAT | O_WRONLY,  0777);
int main();
int main(int argc, char **argv);
int main(int argc, char **argv, char **envp);
int main(int argc, char **argv, char **envp, char **apple);