为什么在同名的 .c 文件中需要包含一个 .h 头文件?

12

我正在跟随《Head First C》的学习,现在我们正在学习如何将多个文件编译在一起,其中之一是encrypt.c

#include "encrypt.h"


void encrypt(char *message)
{
  char c;
  while (*message) {
   *message = *message ^ 31;
   message++;
  }
}

encrypt.h 文件在结尾加上了分号并重复了第一行,那我为什么需要它呢?我知道在使用函数之前定义头文件可以解决问题,这样我就可以理解为什么要在使用encrypt.c的文件中包含它,但是为什么我要在encrypt.c内部也包含它呢?这只是一个“因为”原因吗?


在定义函数之前,您不能使用它。您可以声明函数的头文件,然后在定义后使用它。但是,头文件并不授予您超级用户权限,因为它允许您使用未声明的函数。 - smac89
3
在这个特定的例子中并不相关,但是头文件还经常定义了typedefstruct以及其他函数声明中需要使用的各种内容。 - Crowman
我还有其他的。这本书有缺点,但所有书都有缺点。 - punstress
6个回答

19
如果encrypt.c的内容完整地显示出来,那么你并不需要头文件。但是还是建议包含它,因为:
  1. 如果文件中的一个函数使用另一个函数,则定义的顺序很重要,因为调用者必须在被调用者之前定义。甚至可能有两个函数A和B,其中每个函数都调用另一个函数,这种情况下,你不能在没有至少一次前向声明的情况下编译代码。包含具有前向声明的头文件可以解决这些问题。
  2. 像客户端代码一样使用头文件是一种好的方式,让编译器指出前向声明中的签名与实际函数定义之间的差异。如果未检测到这种问题,可能会导致运行时的“有趣”行为和大量的梳头。

1
我喜欢你的第二点。这本书提出了你关于相互调用的函数的第一点,所以ping调用pong,pong调用ping,我理解头文件如何解决这个问题,但是我想,这里我们只有一个函数,这只是为了让我养成习惯吗? - punstress
1
@punstress:本质上是这样的。养成这个习惯是很好的。;-) - Jon
@Jon 本质上来说,将接口与实现分离开来是一个很好的实践,对吧? - NlightNFotis
1
@NlightNFotis:当然,但这不是重点。此外,“总是”在实际工程中是一个强烈的词语,因为很多时候它可能涉及大量的工作和YAGNI - Jon

6

你说得对,如果encrypt.h只声明了这些内容,那么你不需要在.c文件中包含它。

通常情况下,你这样做是为了保持一致性。


6
假设您修改了encrypt.c文件中的内容为:void encrypt(char *message, int i) { } 如果您没有包含encrypt.h文件,那么您将不会注意到应用程序中的其他文件尚未更新以传递新参数。如果您同时更新encrypt.h和encrypt.c文件,编译器可以为您进行检查。

1
刚刚重新阅读了去年的这篇文章,我很喜欢你的观点。谢谢。 - punstress

2

这是一个很好的编程风格。 有时,实现函数的C文件和使用函数的C文件共享通用声明 - 类型/结构。这些共享声明放在H文件中。 例如:

[enc.h]
typedef enum {S,F} Res;
EN encode();

[enc.c]
#include "enc.h"
Res encode() { ... }

[other.c]
Res res;
res = encode();

0

然后您在另一个*.c文件中包含头文件,编译器就知道函数定义在其他任何地方。

这就像:

#include <stdio.h>

int main (void)
{
   afun();
   return 0;
}

void afun (void)
{
   printf("hello\n");
}

现在编译器不知道在主函数中如何处理 afun(),因为它没有被定义。所以会导致编译器错误。

所以你需要在开头或第一次使用之前添加声明:

#include <stdio.h>

void afun(void);

int main (void)
{
   afun();
   return 0;
}

void afun (void)
{
   printf("hello\n");
}

编译器知道afun的声明,并希望在其他地方定义该函数。使用头文件可以使用预编译的C代码。编译器所需的唯一内容是函数的声明。


1
OP在她的帖子中说:“我明白为什么需要头文件来解决在定义函数之前使用函数的问题”。 - Crowman
是的,我会展示: 使用头文件可以使用预编译的C代码。 - Alex44

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接