.h文件是由编译器区别对待还是“仅”命名约定?

时间:2019-05-06 13:32:01

标签: c++ c header-files

我是C系列编程和编译的新手。我试图了解什么.c / .cpp文件和.h文件处于较低的技术水平。我了解.h文件用于指定接口,而.c或.cpp文件用于实现。但是,我想知道这种区别是否反映在编译器的工作方式上,还是“只是”一种为我们人类简化的命名约定?从理论上讲,您可以在.h文件中包含这个隐含内容,并且仍然可以对其进行编译吗?或在.c文件中指定接口?我要求更好地了解编译器的实际功能。

5 个答案:

答案 0 :(得分:9)

从技术上讲,就编译器而言,任何扩展名之间都没有区别。就像您手动键入放置#include的任何文件的内容一样。您可以键入#include "foo.pdf",并且编译器将成功包含文件foo.pdf,只要该文件包含代码(尽管有扩展名)

按照惯例,通常将声明放在.h / hpp文件(或模板定义)中,并将实现放在.c / cpp文件中。

许多库都具有单文件实现,并借助内联变量/函数。

有时包括文件甚至不存在,例如,当包括标准STL文件(例如string)时,编译器可能根本不读取文件,而是按需要缓存/实现它。

有关MSDNCPPReference#include的更多信息。

答案 1 :(得分:5)

编译器按照您的指示进行操作。它将编译您指定为其输入的文件,并且作为此编译的一部分,如果您使用#include预处理程序指令指示这样做,它将在这些输入文件中包含其他文件。

如果您告诉编译器编译.h文件(并传递命令行选项以使其视为源文件,例如gcc的-x c),它将编译该文件。正好。如果您#include "a.cpp",它将被正常包含。

答案 2 :(得分:2)

它主要只是一个命名约定,可以帮助我们组织代码。至少在用户定义的标头方面没有低级的技术差异(某些实现可能具有“预编译”标头,因此没有可读的源文本)。所有这些都必须是有效的C或C ++代码。 C语言标准使用扩展名.h来命名所有标准库标头,因此大多数人都遵循该标头的惯例。 C ++语言标准使用 no 扩展名(iostreamstring等)来命名所有标准库头,但是大多数人都遵循.h(或{ {1}})命名约定,主要是为了使搜索更容易。

个体工具可能会在意-gcc将.hpp文件与.c文件不同地对待,IDE可能将.cpp文件与.h文件不同地对待-但这是一个该特定工具的功能,而不是语言。

某些(公认的较早的)系统使用的文件命名约定完全不允许.c.h扩展名 -HP3000上的MPE使用了约定{ 1}}。 MPE上的C编译器能够正确映射诸如.cfilename.groupname.accountname之类的标准标头名称,但是用户定义的标头必须遵循stdio.h格式(所有其中必须包含35个以内的字符(包括定界符),以使它们引人入胜,例如stdlib.h

答案 3 :(得分:1)

这确实是对编译器的透明约定,对于使大型项目更具有组织性,这对接口/实现双重性很有用。

特别是在减少C ++设计中的耦合时,这种分离非常实用,这使得将来可以轻松进行(接口/实现)自定义。

分隔对于抽象也很有用,在库的情况下也可以向用户隐藏实现细节,这使得仅访问该接口的用户更方便,并且他们不应该为实现细节而烦恼。

答案 4 :(得分:0)

对于头文件,这只是一个约定。

您还可以包括一些其他代码,其中一个旧模式是:

file.inc:

  MACRO("-a", "--append", "this will append text"),
  MACRO("-b", "--bottom", "something for bottom"),

并在主文件中:

char *options[] = {
#define MACRO(short, long, help) (short)
#include "file.inc"
#undef MACRO
NULL
};

char *help[] = {
#define MACRO(short, long, help) (help)
#include "file.inc"
#undef MACRO
NULL
};

以及此类构造。 现在不再那么频繁了。我认为在 20世纪C 一书中仍然有这样的技巧,但就个人而言,我更喜欢使用外部预处理器。

Linux内核有时包括其他* .c文件,例如用宏修改一些功能。我认为这通常不是一种很好的编码风格,但是内核使用它来构建例如并行驱动程序,它们共享99.9%的代码。

注意:#include <include.h>是不同的。在这种情况下,include.h可以解释为编译器的标签(可以将其用作标志)。系统中不需要标准库头,但是现代的普通编译器也具有标准库头。

h文件可能为空,或者应包含最后一个换行符\n。没有其他要求。对于上下文,它应该是有效的C。

从历史上看,有一个预处理器cpp和一个编译器cc,它们是两个不同的程序。因此,预处理器对文件名约定和结构一无所知。编译而不是编译其余部分,就像单个文件一样。 [有关标准库的可能例外,请参见上文]。注意:我也看到在没有C文件的shell脚本中使用了C预处理器。 (gcc -E

最后一点。一些编译器,例如gcc,使用文件扩展名选择要使用的语言。您可以使用命令行选项覆盖它。但是,gcc a.h无法按照您期望的方式编译文件。例如。在我的系统touch a.h b.c; gcc a.h; gcc b.c上给出了两种不同的结果。