为什么函数中需要原型?

scu*_*zz0 6 c function function-prototypes

我正在学习 C,我的书解释了如何“构建函数原型”,以便我们可以在定义它之前使用它。

关键是我无法想象在定义函数之前需要使用它的情况,为什么我们不能在一开始就定义它呢?

您能否提供一个示例,其中严格需要对函数进行原型设计(如果存在)?

abh*_*aik 7

考虑下面的程序:

#include <stdio.h>

void add(int, int);

int main() {
    add(5, 3);
    return 0;
}

void add(int a, int b) {
    printf("%d", a+b);
}
Run Code Online (Sandbox Code Playgroud)

在这里,当您调用add(5, 3)from时main(),编译器知道它add()是什么 - 它接受的参数和返回类型,这要归功于void add(int, int)第 3 行中的语句。如果该语句被注释掉,那么编译器将不知道是什么add()并且会给您一个错误。因此,第 3 行告诉编译器您add()稍后已经定义了该函数,这使得它完全可以在main(). 或者,如果您在 之前add()编写函数,那么编译器知道您调用它时的内容;因此在这种情况下不需要函数原型。 main()add()

希望这有用。

  • 正是这种情况,省略原型并将“add”函数放在“main”之前非常简单。:) (7认同)
  • @user3629249 我在哪里说过这不是C?我只是说我不喜欢在非常简单的单文件程序中使用原型,因为它没有用,在这种情况下甚至没有头文件。在大型项目中当然必须使用标头,并且源文件中仍然不应该有原型 (2认同)

Pet*_*des 2

一个用例是当您想要将函数指针指向另一个源文件中定义的函数时。(或者用asm手写)。使用函数指针总是需要提前声明该函数,无论是通过定义还是原型。

C89(及更早版本)允许隐式声明函数(即使编译单元中早期没有定义或原型(this .c + 任何你的东西#include)也可以使用它们。

因此,您可以在 C89 中将其单独编译到一个文件中(或者在 C99 / C11 模式下大多数编译器发出警告

int foo() {
    return bar( 123 );   /* implicit declaration of  int bar()  */
}
Run Code Online (Sandbox Code Playgroud)

不能用作&bar函数指针

/* int bar(int);      necessary if you haven't defined  int bar(int x) { ... } */
void *take_fptr() {
     return &bar;    // error: 'bar' undeclared   from GCC
}
Run Code Online (Sandbox Code Playgroud)

您还可以构建需要声明才能正确传递参数的情况,例如,如果您希望函数采用参数float,则需要通过定义或原型来声明它。当将 arg 传递给非原型函数或可变参数函数(如 printf)的一部分时,ISO C 标准中的“默认参数提升”将提升float为。double...

(这就是为什么%f打印双精度数,而不是浮点数,以及为什么%lf如果完全支持的话也是双精度数。也是为什么像这样的函数putchar(int)将它们的 char arg 作为 int:窄整数类型的默认提升到int。C 标准的某些部分库的历史可以追溯到很早期的 C,在语言支持原型之前!)

或者例如,如果您的函数需要 a long long,但调用者写入bar( 123 ),则调用者只会推断int,其传递方式可能与 a 不同long long。但你可以通过写来解决这个问题bar( 123LL ),或者bar( (long long)123 )

或者,如果返回类型不是int(或void),那么您还需要编译器通过声明了解该函数。隐式声明的函数假定返回int。根据调用约定和体系结构,您可能会忽略返回short类型,或者在某些机器上甚至是char*,例如在 32 位机器上,intptr_t其中int。(早期的 C 就依赖于此。)

但一般来说,例如在具有 64 位指针的机器上,隐式 int 返回将不起作用。它也不适用于double返回值或struct.