像这样用 C 语言声明然后定义一个函数的目的是什么?



1(为什么我们要向编译器提供两次相同的信息?2(这不是多余的吗?3( 我们什么时候应该遵守这个规则?4( 我们什么时候可以省略这个双重声明释义


void fx(void);
void fx ( void ){
printf("Hello Worldn");
}

int main(void)
{
fx();

}

1(为什么我们要向编译器提供两次相同的信息?

当C首次设计时,计算机非常有限(例如,以KiB而非MiB或GiB测量的RAM,具有<1MHz时钟的单个CPU,而具有>1GHz时钟的多个CPU,等等(。有很多技巧可以解决这个问题(将"编译"拆分为多个阶段,将"程序"拆分为几个编译单元,以小块形式读取文件,使整个文件不在RAM中,等等(。

其中一个技巧是一次性解析源文件

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

编译器不会有任何线索;CCD_ 1";当其解析"是"时;CCD_ 2";(因为它直到稍后才会看到"bar()"(。解决方案是声明——允许程序员在定义事物之前声明它们,这样编译器就可以完成一次操作,而不会被它还没有看到的东西弄糊涂。

  1. 我们什么时候可以省略这个双重声明和定义

如果编译器在使用该定义之前会看到该定义,则可以省略该声明。例如,这很好:

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

2(这不是多余的吗?

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

在您的情况下,不需要它。

但在这种情况下:

double fx(double x);
int main(void)
{
printf("%fn", fx(4.0)); 
printf("%fn", dx(4.0)); 
}
double fx(double x)
{
return x * x;
}
double dx(double x)
{
return x * x;
}
<source>: In function 'main':
<source>:21:20: warning: implicit declaration of function 'dx'; did you mean 'fx'? [-Wimplicit-function-declaration]
21 |     printf("%fn", dx(4.0));
|                    ^~
|                    fx
<source>: At top level:
<source>:29:8: error: conflicting types for 'dx'; have 'double(double)'
29 | double dx(double x)
|        ^~
<source>:21:20: note: previous implicit declaration of 'dx' with type 'int()'
21 |     printf("%fn", dx(4.0));

https://godbolt.org/z/6ffWf9xG8

一般来说,像这样的功能原型

double fx(double);

1(为什么我们要向编译器提供相同的信息时间?

告诉编译器,代码或库函数fx中的某个位置已经或将被定义,它接受一个double参数并返回double。编译器可以发出正确的代码来调用此函数,传递参数并使用返回值

2(这不是多余的吗

在您的示例中是这样。如果在函数调用之前函数的定义未知,则不是这样。

  1. 我们什么时候应该遵守这个规则

我已经在上面解释过了。

  1. 我们什么时候可以省略这个双重声明和定义

当函数在同一编译单元中定义时,和它在第一次调用之前(作为.c文件中的位置(定义。

一个没有正文的函数声明,如void fx(void);,被称为原型,其目的是通知编译器存在一个具有某些返回类型、名称和可选参数集的函数,它可以在编译期间或之后链接时在其他地方找到这些函数。这些是语言的一部分,因为它们允许程序员模块化地设计软件。

声明函数原型可以防止编译器在调用尚未看到其定义的函数时抱怨,例如:

#include <stdio.h>
int foo(int in); //Without this the compiler will complain and/or refuse to compile
int main(){
printf("%dn",foo(7));
}
int foo(int in){
return in + 1;
}

此外,上面示例的第一行显示#include <stdio.h>,它告诉编译器包含C标准io头文件。stdio.h包含printf原型,其告诉一旦到了链接程序的时间,它将能够找到int printf(const char*,...);形式的函数。

或者,您可以编写单独的文件";foo.c"foo.h";以及";main.c";对于更模块化的方法,比如:

main.c

#include <stdio.h>
#include "foo.h"  //Include .h file to get prototype
int main(){
printf("%dn",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

如果你不想使用原型,你不会被迫使用,但如果你选择不使用它们,你将被要求在程序中的每个函数被另一个函数调用之前声明它。

最新更新