gcc 在C语言中声明并定义一个函数的目的是什么?

e0bqpujr  于 2022-11-12  发布在  其他
关注(0)|答案(3)|浏览(186)

1)为什么我们要两次向编译器提供相同的信息?2)这不是多余的吗?3)何时我们应该遵循这条规则?4)何时我们可以省略这个双精度声明和定义?

void fx(void);

void fx ( void ){

   printf("Hello World\n");
}

int main(void)
{

   fx();
  
}
nlejzf6q

nlejzf6q1#

1)为什么我们要向编译器提供两次相同的信息?
当C语言最初被设计出来的时候,计算机是非常有限的(例如RAM是以KiB而不是MiB或GiB来衡量的,单个CPU的时钟频率〈1 MHz而不是多个CPU的时钟频率〉1 GHz,等等)。有很多技巧可以科普这个问题(将“编译”分成多个阶段,将“程序”分成多个编译单元,以小块来阅读文件,这样整个文件就不在RAM中了,等等)。
其中一个技巧是一次解析源文件。这意味着,如果您执行以下操作:

int foo(int x) {
    return bar(x);
}

int bar(int x) {
    return x;
}

..编译器在解析“foo()“时不会知道“bar()“是什么(因为它直到稍后才看到“bar()“)。解决办法是声明-允许程序员在定义之前声明,这样编译器就可以只做一遍,而不会被它还没有看到的东西弄糊涂。
1.什么时候可以省略这个双精度声明和定义?
如果编译器会在使用定义之前看到该定义,则可以省略声明。例如,可以这样做:

int bar(int x) {
    return x;
}

int foo(int x) {
    return bar(x);
}

2)它不是多余的吗?
它是多余的,但根据C语言规范是必要的,因为它在50多年前是必要的(为了使编译器在“资源极度受限”的计算机上实用);即使它已经有30多年没有意义了,而且更新的语言也不需要它。

qhhrdooz

qhhrdooz2#

在您的情况下,则不需要。
但在这种情况下:
第一个
https://godbolt.org/z/6ffWf9xG8
通常,函数原型(如

double fx(double);

1)为什么我们要向编译器提供两次相同的信息?
告诉编译器在代码或库中的某个地方定义了或将要定义函数fx,并且该函数采用双精度型参数并返回双精度型。编译器可以发出正确的代码来调用此函数,传递参数并使用返回值
2)是否冗余
在你的例子中,它是。如果在函数调用之前不知道函数的定义,它就不是。
1.我们什么时候应该遵守这条规则?
我已经在上面解释过了。
1.什么时候可以省略这个双精度声明和定义?
当函数在同一编译单元中定义时,它定义在第一次调用之前的之前(与.c文件中的位置相同)。

dvtswwa3

dvtswwa33#

一个没有函数体的函数声明,比如void fx(void);,被称为 prototype,它的目的是通知编译器存在一个函数,该函数具有某种返回类型、某种名称,以及可选的一些参数集,它可以在编译过程中或之后链接时在其他地方找到这些参数集。这些都是语言的一部分,因为它们允许程序员模块化地设计他们的软件。
声明函数原型可以防止编译器在您调用尚未看到其定义的函数时发出抱怨,例如:

#include <stdio.h>
int foo(int in); //Without this the program will not compile

int main(){
  printf("%d\n",foo(7));
}

int foo(int in){
  return in + 1;
}

另外,上面例子的第一行是#include <stdio.h>,它告诉编译器包含C标准的io头文件。stdio.h包含printfprototype,它告诉编译器一旦链接程序,它将能够找到int printf(const char*,...);形式的函数。
或者,您可以编写单独的文件“foo.c”、“foo.h”和“main.c”,以实现更模块化的方法,如下所示:

主目录.c

#include <stdio.h>
#include "foo.h"  //Include .h file to get prototype

int main(){
  printf("%d\n",foo(7));
}

foo.h

#ifndef FOO_H
#define FOO_H

int foo(int in); //Prototype of foo()

#endif

foo.c

#include "foo.h"

int foo(int in){ //Declatation of foo()
  return in + 1;
}

然后你可以将foo.c编译成一个 * 目标文件 *,并将它和main.c沿着传递给编译器,如下所示:

gcc -c foo.c
gcc -o main main.c foo.o

如果你不想使用 * prototype *,你不会被迫使用它们,但是如果你选择不使用它们,你将被要求在你的程序中声明每个函数,然后在另一个程序中调用它。

相关问题