你提出的问题实际上是两个问题,而不是一个问题。到目前为止,大多数回复都试图用通用的毯子“这是K&R风格”来回答整个问题,而实际上只有一小部分与所谓的K&R风格有关(除非你以某种方式将整个C语言看作是“K&R风格”):
第一部分是函数中使用的奇怪语法。定义
int func(p, p2)void *p;int p2; /* <- optional in C89/90, but not in C99 */{
return 0;}
这是一个K&R型函数的定义。其他答案也很好地说明了这一点。其实也没什么大不了的。语法不受欢迎,但即使在C99中仍然完全支持(C99中的“无隐式int”规则除外),这意味着在C99中您不能省略p2
).
第二部分与K&R风格无关。我指的是可以用“交换”参数调用函数,即在这样的调用中不进行参数类型检查。这与K&R风格的定义本身没有什么关系,但它与您的功能没有原型有关。您看,在C中,当您声明这样的函数时
int foo();
它实际上声明了一个函数foo
这需要未知类型的未指定数目的参数..你可以称之为
foo(2, 3);
和AS
j = foo(p, -3, "hello world");
ANS等等(你有这个想法);
只有带有适当参数的调用才能“工作”(这意味着其他调用会产生未定义的行为),但完全由您来确保其正确性。即使编译器以某种方式神奇地知道正确的参数类型及其总数,也不需要对不正确的参数进行诊断。
实际上,这种行为是特征C语言。一个危险的,但仍然是一个特征。它允许你做这样的事情
void foo(int i);void bar(char *a, double b);void baz(void);int main(){
void (*fn[])() = { foo, bar, baz };
fn[0](5);
fn[1]("abc", 1.0);
fn[2]();}
也就是说,在没有任何类型转换的“多态”数组中混合不同的函数类型(不过,在这里不能使用各种函数类型)。同样,这种技术的内在危险是显而易见的(我不记得曾经使用过它,但我可以想象它在哪里是有用的),但这毕竟是C。
最后,将答案的第二部分与第一部分联系起来的位。当你做一个K&R风格的函数定义时,它不会为这个函数引入一个原型。就函数类型而言,func
定义声明func
如
int func();
也就是说,既不声明参数的类型,也不声明参数的总数。在您最初的帖子中,您会说“.它似乎指定了它使用了多少个参数.”。从形式上说,它不是!在你的双参数K&R风格之后func
定义仍然可以调用func
如
func(1, 2, 3, 4, "Hi!");
也不会有任何违反约束的地方。(通常,高质量的编译器会给您一个警告)。
另外,一个有时被忽视的事实是
int f(){
return 0;}
也是一个K&R风格的函数定义,不引入原型。要使它成为“现代”,你必须明确地指出void
在参数列表中
int f(void){
return 0;}
最后,与流行的观点相反,在C99中完全支持K&R风格的函数定义和非原型函数声明。如果我没记错的话,前者自C89/90以来就被否决了。C99要求在第一次使用之前声明函数,但声明不需要是原型。这种混淆显然源于流行的术语混淆:许多人称任何函数声明为“原型”,而实际上“函数声明”与“原型”并不相同。