C 中源文件和头文件的根本区别是什么?

问题描述 投票:0回答:5

我不太明白 C 的源文件和头文件中的内容应该如何分开。我经常看到许多项目有两组同名的文件(一个扩展名表示源文件,另一个表示头文件)。

到目前为止,由于缺乏理解,当我编写库时,我将所有类和类方法代码都放入一个文件中,对于选择文件扩展名犹豫不决。

标头中应包含哪些内容以及源文件中应包含哪些内容?我如何实现这种分离?

c
5个回答
104
投票

没有技术差异。如果您愿意,编译器会很乐意让您包含

.c
文件,或者直接编译
.h
文件。

然而,存在巨大的文化差异:

  • 声明(原型)进入

    .h
    文件。
    .h
    文件是相应 .c 文件中实现的任何内容的
    接口

  • 定义进入

    .c
    文件。他们实现
    .h
    文件中指定的接口。

不同之处在于,一个

.h
文件可以(并且通常会)被
#include
分成多个编译单元(
.c
文件)。如果您在 .h 文件中
定义 
函数,它将最终出现在多个
.o
文件中,并且链接器会抱怨多重定义的符号。这就是为什么定义不应该放在
.h
文件中。 (内联函数是例外。)

如果某个函数在

.c
文件中定义,并且您想从其他
.c
文件中使用该函数,则该函数的声明需要在其他每个
.c
文件中可用。这就是为什么您将声明放在
.h
中,并将
#include
放在每个声明中。您也可以在每个
.c
文件中重复声明,但这会导致大量代码重复和难以管理的混乱。

如果某个函数在

.c
文件中定义,但您不想 希望从其他
.c
文件中使用它,则无需在标头中声明它。它本质上是该
.c
文件的实现细节。在这种情况下,也将函数设为
static
,这样它就不会与其他文件中的同名函数冲突。


16
投票

标头中应包含哪些内容以及源文件中应包含哪些内容?

标头通常包含以下一项或多项:

  • 函数声明(静态除外)
  • 变量声明(通常是全局的)
  • 用户定义的类型声明(阅读
    struct
    union
    等)
  • 宏定义

另一方面,源文件有:

  • 函数/变量定义
  • 静态函数声明和定义(您不想将这些暴露给客户)
  • 变量定义
  • 有些人喜欢在标头中定义内联函数(C99)

如何实现这种分离?

单一定义规则是你的朋友。

请记住,如果您正在编写一个库,这就是您的客户看到的内容。因此,请提供帮助并提供所有信息,以便他们使用您的图书馆。源文件通常以二进制形式编译和提供。

顺便说一句,C 没有类的概念。


2
投票

通常,头文件包含声明,源文件包含代码。

因此,如果在源文件

A.c
中您需要在源文件
B.c
中实现一个函数,则只需包含
B.h
即可获得其声明。


2
投票

.c 和 .h 文件之间几乎没有根本区别(尽管某些编译器可能拒绝编译原始 .h 文件)。按照惯例,差异更大。

通常,.h 文件提供 API,.c 提供实现。

因此 .h 文件将仅包含其他源文件访问 .c 文件提供的功能所需的内容。因此 .h 文件将提供全局函数的函数原型、全局变量的声明(如果您确实必须拥有它们)以及它们使用的结构和其他类型。 (如果 API 只需要指向结构的指针,则不要公开该结构。)

内联函数也经常包含在 .h 文件中,但一些编码指南更喜欢使用单独的扩展名(例如 .inl)

所有其他函数实现、变量的定义和初始化以及局部(静态)变量和函数的声明都将位于 .c 文件中。


0
投票

其他答案都很好,但我错过了一个额外因素:

  • 源文件是带有源代码的文件,旨在转换为翻译单元。然后每个翻译单元由编译器编译成机器代码。

  • 头文件是带有源代码的文件,旨在通过#include预处理器指令

    包含
    到其他文件(源文件或头文件)中。

这种区别非常重要,可能有助于理解如何分离两种类型文件之间的代码。

© www.soinside.com 2019 - 2024. All rights reserved.