【问题标题】:Function declaration: K&R vs ANSI [duplicate]函数声明:K&R vs ANSI [重复]
【发布时间】:2011-03-06 17:47:18
【问题描述】:

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

【问题讨论】:

标签: c kr-c


【解决方案1】:

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函数定义,不是K&R函数声明。
  • 请注意,也允许使用 K&R 代码:int foo(a, p) char *p; { … }。未明确定义类型的参数被假定为int。还要注意,您可以写:foo (a, b, c, d, e, f, g) double f; char *e, *b; { … },不仅缺少返回类型(假定为 int),而且还缺少许多参数的显式类型(acdg — 因此假定为 int),并以与参数在参数列表中出现的顺序无关的顺序列出其他参数的类型。
【解决方案2】:

旧版 K&R 样式声明/定义

当 Kernighan 和 Ritchie 第一次发表“The C Programming Language”时,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 在this answer 中针对类似问题指出,该语法已被弃用,但自 C99 起仍然合法(并且指向具有未知数量和类型参数的函数的函数指针仍有潜在应用,尽管存在未定义行为的高风险);因此,如果在没有适当原型的情况下声明或调用函数,兼容的编译器最多会产生警告。

在没有原型的情况下调用函数不太安全,因为编译器无法验证您是否以正确的顺序传递了正确数量和类型的参数;如果调用实际上不正确,则会导致未定义的行为。

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

// 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;
}

【讨论】:

  • 您需要澄清“旧版本的 C”是什么意思。你说:旧版本的 C 语言不需要完整的函数原型,但在调用之前仍然需要函数的前向声明。 但是旧版本的 C,意味着预标准C,根本没有原型,并且在调用之前不需要函数的前向声明。甚至 C89/C90 也不需要这样的前向声明。 C99 要求在使用前声明函数(但仍不强制原型)。
  • @JonathanLeffler 感谢您的提示,我已经阅读了更多内容(主要是关于 K&R 时代 C 的 Wikipedia 文章)并清理了它,我认为它现在应该在历史上是准确的。
  • @PeterCordes 这就是这个答案的重点......函数的“非原型”声明与函数的“K&R”声明完全相同。我用一整节来讨论意外的非原型声明。
  • 啊,是的,我浏览得太快了,因为我已经知道这一点,只看了一个代码块就发表评论真是太愚蠢了。
【解决方案3】:

我只想在传统的 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。

--来源:https://en.wikipedia.org/wiki/C_(programming_language)#K.26R_C

这可以说是一种遗留的编码风格,由于清晰度问题应该避免,但旧的算法教科书往往偏爱这种 K&R 风格。

【讨论】:

  • 对于受虐狂来说,你可以拥有main(argc, argv) char **argv; { … },因为没有明确类型规范的参数类型是int。另外,function(a, b, c, d) char *d, double c, a; { … } 也很好;名称不必按照它们在参数列表中出现的顺序列在类型部分中。
  • 您应该将@JonathanLeffler 的评论合并到您的答案中。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-08
  • 2020-11-01
  • 1970-01-01
  • 2011-05-01
  • 2021-10-17
相关资源
最近更新 更多