C89,C90或C99中的所有功能都需要原型吗?

时间:2009-01-12 08:09:42

标签: c c99 c89

要真正符合标准,C中的所有函数(main除外)都必须有原型,即使它们只是在同一个翻译单元中定义之后才使用吗?

6 个答案:

答案 0 :(得分:37)

这取决于“真正符合标准”的含义。然而,简短的回答是“在使用之前确保所有函数都具有范围原型是一个好主意”。

更合格的答案指出,如果函数接受变量参数(特别是printf()函数族),那么原型必须在范围内严格遵守标准。 C89(来自ANSI)和C90(来自ISO;与C89相同,除了章节编号)也是如此。但是,除了'varargs'函数之外,不必声明返回int的函数,并且返回除int以外的函数的函数确实需要一个声明来显示返回类型但不需要需要原型作为参数列表。

但是,请注意,如果函数在没有原型的情况下接受了“正常促销”的参数(例如,需要charshort的函数 - 两者都是被转换为int;更严重的是,一个函数需要float而不是double),然后需要原型。标准是松散的,允许旧的C代码在标准符合的编译器下编译;旧代码并不是为了确保在使用前声明函数而编写的 - 根据定义,旧代码没有使用原型,因为在有标准之前它们没有在C中可用。

C99不允许'隐式int'...这意味着两个奇怪的情况,如'static a;'(默认为int)以及隐式函数声明。在ISO / IEC 9899:1999的前言中提到了这些(以及大约50个其他主要变化), 将该标准与以前的版本进行比较:

  
      
  • 删除隐式int
      ......
  •   
  • 删除隐式函数声明
  •   

在ISO / IEC 9899:1990中,§6.3.2.2函数调用声明:

  

如果函数调用中带括号的参数列表之前的表达式包含   仅仅是标识符,如果该标识符没有可见的声明,则隐式标识符   在包含函数调用的最内层块中声明完全相同的声明:

extern int identifier();
     

出现。 38

     

38 也就是说,具有块作用域的标识符声明与类型函数没有外部链接   参数信息并返回int。如果实际上它没有被定义为具有“功能”类型   返回int,“行为未定义。

1999年标准中缺少此段。我(尚未)跟踪C90中允许static a;的措辞变化,并在C99中禁止它(需要static int a;)。

请注意,如果函数是静态的,则可以在使用它之前定义它,并且不需要在声明之前。如果定义了非静态函数而没有在其前面的声明(-Wmissing-prototypes),则可以说服GCC。

答案 1 :(得分:14)

prototype 是一个函数声明,它指定函数参数的类型。

预ANSI-C(1978年第一版Kernighan& Ritchie所描述的语言“The C Programming Language”)没有原型;函数声明不可能描述参数的数量或类型。由调用者传递正确数量和类型的参数。

ANSI C引入了“prototypes”,这些声明指定了参数的类型(从早期C ++中借用的一个特性)。

从C89 / C90(ANSI和ISO标准描述相同的语言)开始,调用没有可见声明的函数是合法的;提供了隐式声明。如果隐式声明与实际定义不兼容(比如调用sqrt("foo"),那么行为是未定义的。这个隐式声明和非原型声明都不能与可变参数函数兼容,因此对可变参数的任何调用都是如此函数(如printfscanf)必须具有可见的原型。

C99删除了隐式声明。对没有可见声明的函数的任何调用都是违反约束的,需要编译器诊断。但该声明仍然不需要成为原型;它可以是旧式声明,不指定参数类型。

C11在这方面没有发生重大变化。

因此,即使是2011 ISO C标准,仍然允许在符合规范的代码中使用旧式函数声明和定义(自1989年以来“过时”)。

对于可追溯到1989年的所有C版本,作为一种风格问题,没有理由不将原型用于所有功能。保留旧式声明和定义只是为了避免破坏旧代码。

答案 2 :(得分:13)

不,功能并不总是需要原型。唯一的要求是在使用函数之前“声明”它。声明函数有两种方法:编写原型或编写函数本身(称为“定义”。)定义始终是一个声明,但并非所有声明都是定义。

答案 3 :(得分:1)

是的,每个函数都必须有一个原型,但该原型可能出现在单独的声明中,也可能出现在函数定义的一部分中。用C89编写的函数定义自然会有原型,但如果你用经典的K& R风格写东西,那么:

main (argc, argv)

  int argc;
  char **argv;

{
  ...
}

然后函数定义没有原型。如果你写ANSI C(C89)样式,那么:

main (int argc, char **argv) { ... }

然后函数定义有一个原型。

答案 4 :(得分:1)

编写新功能时的一个很好的提示是将它们倒置在底部使用main,这样当你改变主意关于函数的args或返回类型时,你不必修复原型太。不断修复原型,并在它们过时时处理所有编译器的警告变得非常繁琐。

让函数顺利运行后,将代码移动到一个命名良好的模块,并将原型放在同名的.h文件中。它节省了大量时间。我在5年内发现的最大生产力援助。

答案 5 :(得分:0)

据我所知(在ANSI C89 / ISO C90中),没有。我不确定C99;但是,我希望也一样。

个人注意:我只在......时编写函数原型。

  1. 我需要(当A()调用B() B()调用A())或
  2. 我正在导出这个功能;否则,感觉多余。