我有两个疑问:
答案 0 :(得分:8)
C和C ++是语言,广泛使用称为功能转发的功能。这意味着,您可以说,例如:
void f(int i); /* note the semicolon */
这意味着:“我保证,在源代码后面的某个地方有人会定义,函数f实际上做了什么”。在这样的转发之后,您可以使用此函数,因为它确实存在,编译器将要求某人以后实际定义此函数(如果没有定义,编译将失败)。此转发也称为标头,因为它实际上是函数定义的标头:
void f(int i) /* Header */
/* Body */
{
/* ... */
}
头文件是一个文件,主要包含此类转发。您可以使用头文件来访问其他位置定义的函数(例如,在不同的编译单元或外部库中),然后附加所需的目标文件或库以提供这些头文件的实现。除了函数转发之外,在头文件中还可以找到正确使用已定义函数所需的结构定义,常量或其他项。
编译器如何将您的转发与.c文件中的实际实现相匹配?简单地说 - 由标题。它试图找到一个函数定义(实现),它与前面声明的头完全匹配。
如果#include
头文件是编译器(特别是预处理器)将头文件的全部内容复制到位,您放置#include
,会发生什么情况。这一切都很神奇,没有更多的事情发生。
在运行时,头文件根本不重要,因为您的可执行文件只包含可执行代码。编译器要么加载库中可用的所有函数(可以通过头文件访问),要么(如果打开优化,则很可能)只选择您在代码中实际使用的这些函数。
有趣的是,只有当某人实际使用该功能时,编译器才需要函数定义(实现)。否则,前转将被忽略。尝试:
void f(int i);
int main(int argc, char ** argv)
{
/* Do not use f here */
return 0;
}
答案 1 :(得分:6)
头文件实际包含什么内容?所有函数定义还是只有原型声明?
头文件包含函数声明,外部变量,宏,结构等。最佳做法是保持函数定义在.c
文件中。
当我包含头文件时会发生什么?是否将头文件的所有内容附加到我的代码中?或者是否将头文件的特定(或全部)内容加载到内存中并根据我的代码调用函数?
头文件只不过是在你使用#include
的地方插入它们的内容。如果您愿意,可以自己编写所有内容。
包含头文件等于复制头文件的内容,但我们不这样做,因为它会非常容易出错,并且在源文件中复制头文件的内容不是一个好主意,特别是如果我们有多个包含我们程序的源文件。
修改强>
对于完整执行程序的内存布局,您可以点击此链接http://fgiasson.com/articles/memorylayout.txt
答案 2 :(得分:2)
头文件应包含有用的函数声明(可能还有变量)和类型定义(struct
,union
,enum
或typedef
)和宏(类似函数或类似对象的宏)。这些通常声明某些库的某些部分提供的设施。例如,标准标题(例如<stdio.h>
)描述了标准库的各个部分。现代标题可能包含inline
函数定义,但除此之外,标题既不应定义函数也不应定义变量 - 它应该只声明它们。
另请注意,公共标头通常只应声明对源代码的使用者有用的类型和函数。它应该只包括其他标题,这些标题提供了使用标题中的工具所必需的声明;它不应该(通常)包括只有标头所描述的设施的实现所需的无关标题。
将头文件(及其包含的任何头文件)的内容复制到翻译单元(TU)中。注意到#define
宏和条件处理(#if
等)。您通常可以找到一种方法来查看给定源文件上的预处理器的输出,以查看C编译器实际看到的内容(但要小心;它可能非常详细)。例如,选项通常是-E
(POSIX需要c99
命令)或-P
(较旧的常用选项)。
TU是编译器正确看到的。它包含文件中的源代码以及您包含的标题中的相关信息。
答案 3 :(得分:0)
只是旁注,可能很明显......
ad 2 /如果你在includedig中#include包含#include指令的文件 文件由包含的文件重新定义。这就是发生的事情。这是 预处理器的功能。通过定义
ad 1 /头文件可以继续任何事情(编译器可以做任何事情) 接受)。其他答案描述了惯常惯例 组织c和h文件之间的代码,但这些只是约定。 他们非常善于坚持,你可能会迷失方向 如果你不这样做更大的项目。
实际上,如果你为一个非常简单的微控制器写c,你可以完全没有头文件及其包含它。
如果您是学生/爱好者/有时间,可能有用,分别运行预处理器查看itermediate输出。 有趣的事实:很久以前我曾经使用c预处理器生成coustomized字母。
无论如何,我的观点是:预处理器的作用是由标准定义的,同时在文件之间交换代码,这是您/团队可以在一定程度上选择的约定。这种区别很重要。