C 功能声明:K&;R vs ANSI

C 功能声明:K&;R vs ANSI,c,kr-c,C,Kr C,K&R函数声明和ANSI函数声明有什么区别?K&R语法已经过时,除非您必须维护非常旧的代码,否则可以跳过它 // K&R syntax int foo(a, p) int a; char *p; { return 0; } // ANSI syntax int foo(int a, char *p) { return 0; } 传统K&R风格声明/定义 当Kernighan和Ritchie第一次出版《C编程语言》时,C还没有提供完整的函

K&R函数声明和ANSI函数声明有什么区别?

K&R语法已经过时,除非您必须维护非常旧的代码,否则可以跳过它

// K&R syntax
int foo(a, p) 
    int a; 
    char *p; 
{ 
    return 0; 
}

// ANSI syntax
int foo(int a, char *p) 
{ 
    return 0; 
}

传统K&R风格声明/定义

当Kernighan和Ritchie第一次出版《C编程语言》时,C还没有提供完整的函数原型。存在函数的前向声明,但其唯一目的是指示返回类型。对于返回
int
的函数,直到C99才需要它们

C89增加了函数原型的概念,它还指定了参数的类型(以及隐含的参数数量)。由于原型也是函数声明的一种类型,因此非官方术语“K&R函数声明”有时用于表示并非原型的函数声明

// K&R declarations, we don't know whether these functions have parameters.
int foo(); // this declaration not strictly necessary until C99, because it returns int
float bar();

// Full prototypes, specifying the number and types of parameters
int foo(int);
float bar(int, float);

// K&R definition of a function
int foo(a)
    int a; // parameter types were declared separately
{
    // ...
    return 0;
}

// Modern definition of a function
float bar(int a, float b) 
{
    // ...
    return 0.0;
}
意外K&R声明

值得注意的是,C的新手在打算使用完整原型时可能会意外地使用K&R声明,因为他们可能没有意识到必须将空参数列表指定为
void

如果将函数声明和定义为

// Accidental K&R declaration
int baz(); // May be called with any possible set of parameters

// Definition
int baz() // No actual parameters means undefined behavior if called with parameters.
          // Missing "void" in the parameter list of a definition is undesirable but not
          // strictly an error, no parameters in a definition does mean no parameters;
          // still, it's better to be in the habit of consistently using "void" for empty
          // parameter lists in C, so we don't forget when writing prototypes.
{
    // ...
    return 0;
}
…那么您实际上并没有为不接受参数的函数提供原型,而是为接受未知数量的未知类型参数的函数提供K&R样式的声明

AnT在一个类似的问题中指出,该语法已被弃用,但从C99起仍然合法(并且指向参数数量和类型未知的函数的函数指针仍然有潜在的应用,尽管存在未定义行为的高风险);因此,如果在没有适当原型的情况下声明或调用函数,兼容编译器最多只能产生警告

// K&R declarations, we don't know whether these functions have parameters.
int foo(); // this declaration not strictly necessary until C99, because it returns int
float bar();

// Full prototypes, specifying the number and types of parameters
int foo(int);
float bar(int, float);

// K&R definition of a function
int foo(a)
    int a; // parameter types were declared separately
{
    // ...
    return 0;
}

// Modern definition of a function
float bar(int a, float b) 
{
    // ...
    return 0.0;
}
在没有原型的情况下调用函数不太安全,因为编译器无法验证您是否以正确的顺序传递了正确数量和类型的参数;如果调用实际上不正确,则会产生未定义的行为

声明和定义无参数函数的正确方法当然是:

// Modern declaration of a parameterless function.
int qux(void);  // "void" as a parameter type means there are no parameters.
                // Without using "void", this would be a K&R declaration.

// Modern definition of a parameterless function
int qux(void)
{
    // ...
    return 0;
}

我只想补充一点,在传统的K&R风格中,对于返回
int
值的函数,甚至不需要类型修饰符

考虑一下简单HelloWorld程序的现代C11符号:

int main(int argc, char **argv) {
    printf("hello world\n");
    return 0;
}
这相当于K&R符号样式:

main(argc, argv)
int argc;
char **argv;
{
 printf("hello world\n");
 return 0;
}
请注意,忽略
main()
之前的
int
,但代码仍然可以编译。这是K&R定义的一部分

引用维基百科:

在早期版本的C中,如果在函数定义之前使用,则只需要声明返回非int值的函数;如果使用的是int类型的值,则假定未使用任何先前声明的函数返回int类型

--资料来源:


这可以说是一种传统的编码风格,由于清晰性问题,应该避免使用,但旧的算法教科书通常倾向于使用这种K&R风格。

您能给出每种风格的示例吗?一个相关的问题:它是“K&R”,而不是“knr”(我已经为您解决了这个问题),它代表Kernighan和Ritchie,通常指的是他们在1978年出版的关于C语言的开创性著作《C编程语言》中描述的C语法。本文可能对您有用:实际上,这是一个K&R函数定义,而不是K&R函数声明;{…}。未明确定义类型的参数假定为
int
。也请注意,您可以编写:
foo(a,b,c,d,e,f,g)double f;字符*e,*b;{…}
,不仅缺少返回类型(假定为
int
),而且还缺少许多参数的显式类型(
a
c
d
g
,因此假定为
int
),并以与参数列表中参数出现的顺序无关的顺序列出其他参数的类型。您需要澄清“C的旧版本”是什么意思。您会说:旧版本的C语言不需要完整的函数原型,但在调用函数之前仍然需要函数的前向声明。但是旧版本的C,也就是标准前的C,根本没有原型,也不需要在调用函数之前进行前向声明。甚至C89/C90也不需要这样的转发声明。C99在使用前需要声明函数(但仍然没有强制要求原型)。@JonathanLeffler谢谢你的提示,我已经读了更多(主要是维基百科关于K&R时代C的文章)并清理了它,我认为它现在应该是历史上准确的了。@PeterCordes这就是这个答案的全部要点……一个“非原型”函数的声明与函数的“K&R”声明完全相同。我花了整整一节的时间讨论意外的非原型声明。啊,是的,我浏览得太快了,因为我已经知道了,我真傻,只看了一个代码块就发表评论。对于受虐狂,你可以使用
main(argc,argv)char**argv;{…}
由于没有显式类型规范的参数类型推断为
int
。另外,
函数(a,b,c,d)char*d,double c,a;{…}
也不错;名称不必按照它们在参数列表中出现的顺序列在类型部分。您应该将@JonathanLeffler的注释合并到您的答案中。