我刚接触C家族编程和编译。我试图在低技术水平上理解.c/.cpp文件与.h文件之间的区别。我知道.h文件用于指定接口,而.c或.cpp文件用于实现。但我想知道这种差异是否反映在编译器的工作方式上,还是“只是”一种命名约定,以简化我们人类的操作?你能否在理论上将实现放在.h文件中并仍然进行编译?或者在.c文件中指定接口?我的问题是为了更好地理解编译器实际执行的操作。
我刚接触C家族编程和编译。我试图在低技术水平上理解.c/.cpp文件与.h文件之间的区别。我知道.h文件用于指定接口,而.c或.cpp文件用于实现。但我想知道这种差异是否反映在编译器的工作方式上,还是“只是”一种命名约定,以简化我们人类的操作?你能否在理论上将实现放在.h文件中并仍然进行编译?或者在.c文件中指定接口?我的问题是为了更好地理解编译器实际执行的操作。
从技术上讲,对于编译器来说,任何扩展名之间没有区别。这就像在放置#include
的位置手动键入任何文件的内容一样。您可以键入#include "foo.pdf"
,只要该文件包含代码(尽管扩展名不同),编译器就会成功地包含一个名为foo.pdf的文件。
现在,按照惯例,通常将声明放在.h / hpp文件中(或模板定义中),并将实现放在.c / cpp文件中。
许多库具有单个文件实现,还使用内联变量/函数进行支持。
有时候甚至连包含文件都不存在,例如当包含标准STL文件(如string
)时,编译器可能根本不读取文件,而是以它想要的方式进行缓存/实现。
更多关于#include
的信息请参见MSDN和CPPReference。
#include "/dev/tty"
. 仅适用于类Unix系统;我想知道在Windows下是否有相当的代码(如果有的话) :-) - alx - recommends codidact编译器会按照你的指令进行操作。如果你明确地指定文件输入,编译器会将这些文件进行编译,并在其中包含其他文件,前提是你使用 #include
预处理指示告诉它这样做。
如果你让编译器编译一个 .h
文件(并通过命令行选项使其作为源文件进行处理,例如对于 gcc 使用 -x c
),编译器会正常编译它。如果你使用 #include "a.cpp"
,它会被正常包含。
.h
文件时,需要使用-x c
,因为编译器不会将其视为C源文件由于扩展名。因此,是的,GCC会以不同的方式处理.h
文件和.c
文件。 - Eric Postpischil.h
扩展名命名所有标准库头文件,因此这是大多数人遵循的惯例。 C++语言标准使用无扩展名命名所有标准库头文件(例如iostream
、string
等),但大多数人遵循.h
(或.hpp
)命名惯例,主要是为了使搜索更容易。.c
文件和.cpp
文件,IDE可能会以不同的方式显示.h
文件和.c
文件——但这是特定工具的功能,而不是语言的功能。.h
或.c
扩展名——HP3000上的MPE使用约定filename.groupname.accountname
。 MPE上的C编译器能够正确映射标准头文件名称,例如stdio.h
和stdlib.h
,但用户定义的头文件必须遵循filename.groupname.accountname
格式(所有这些都必须适合35个字符或更少,包括分隔符,导致出现非常易读的名称,例如MYCODEHDR.DEVELOP.BODE
)。这确实只是编译器的透明约定,对于让大型项目更有组织性的接口/实现二元性非常有用。
在C++设计中,分离实践尤其有助于减少耦合,从而方便未来进行(接口/实现)自定义。
对于抽象化,即在库的情况下隐藏实现细节以使用户更方便使用接口,这种分离也非常有用。用户仅能访问接口,不需要理会实现细节。
这只是一个惯例,针对头文件而言。
您还可以包含一些其他代码,旧的模式是:
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
是两个不同的程序。因此,预处理器对文件名惯例和结构一无所知。编译器将编译其余部分,就像单个文件一样。[有关标准库的可能异常,请参见上文]注意:我还看到在shell脚本中使用C预处理器处理非C文件。(gcc -E)
最后一点。某些编译器(如gcc)使用文件扩展名来选择要使用的语言。您可以使用命令行选项覆盖它。但是,gcc a.h
可能无法按您期望的方式编译文件。例如,在我的系统上touch a.h b.c;gcc a.h; gcc b.c
会得到两个不同的结果。