【问题标题】:Why do we need prototype in function?为什么我们需要函数原型?
【发布时间】:2017-03-17 22:45:56
【问题描述】:

我正在学习 C,我的书正在解释如何“对函数进行原型设计”,以便我们可以在定义它之前使用它。

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

您能否提供一个示例,在该示例中对函数进行原型制作(如果存在)是绝对必要的吗?

【问题讨论】:

  • 想象一下,你有 is_even(int n)is_odd(int n) 相互定义。你会怎么写?
  • 请注意,您可以使用没有原型的函数,只需声明一个函数(即使没有原型)。 (但这已被弃用。)
  • 您刚刚开始学习,但您很快就会发现 C 程序通常不会写在单个文件中。这使得“在使用它们之前总是定义它们”是不切实际的。
  • 因为函数定义可能不是本地的,或者已经可见。如果函数在库中,编译器无法访问其定义,因此编译器需要原型才能知道如何接口。
  • 考虑两个以触发器方式相互调用的本地函数。它们不能知道彼此的接口,除非它们中的至少一个具有函数原型,因为 c 是一次性编译器。

标签: c function function-prototypes


【解决方案1】:

考虑下面的程序:

#include <stdio.h>

void add(int, int);

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

void add(int a, int b) {
    printf("%d", a+b);
}

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

希望这是有用的。

【讨论】:

  • 这种情况确实很简单,可以省略原型,只需将add 函数放在main 之前。 :)
  • @Groo,我已经编辑了答案以包括您的 cmets。谢谢。
  • 在 Pascal 中,您没有原型,并且必须始终在 main 之前声明函数。我更喜欢这种方式,并且从不在单文件程序中使用原型。原型只能在复杂项目的头文件中
  • @LưuVĩnhPhúc,在单个文件中可以“摆脱”很多东西,非常简单,程序。然而,真正的程序(不是用于教授概念的简单的家庭作业程序)不能按照您想象的方式编写。对于gcc,参数:Wmissing-prototypes 将导致编译器在任何原型丢失时抱怨,包括:int main()。编译器会想要int main( void ),还有一件事,这是C,而不是Pascal
  • @user3629249 我在哪里说过这不是 C 语言?我只是说我不喜欢在非常简单的单文件程序中使用原型,因为它没有用,在这种情况下甚至没有头文件。在大项目中当然必须使用头文件,而且源文件中应该没有原型
【解决方案2】:

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

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

所以您可以在 C89(或 with a warning from most compilers in C99 / C11 mode)中单独将其编译到一个文件中

int foo() {
    return bar( 123 );   /* implicit declaration of  int bar()  */
}

但是你不能使用&amp;bar作为函数指针

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

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

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

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

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

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

【讨论】:

    【解决方案3】:

    当时可能还不知道函数的定义。想象一个调用malloc 分配内存的程序。在编译时,没有人知道程序在运行时将使用什么分配器。那么如何定义函数呢?

    【讨论】:

    • 我认为您在这里有所作为,但请多解释一下!!!!
    猜你喜欢
    • 2018-03-16
    • 1970-01-01
    • 1970-01-01
    • 2016-09-24
    • 1970-01-01
    • 2020-12-17
    • 1970-01-01
    • 2019-06-09
    • 1970-01-01
    相关资源
    最近更新 更多