繁体   English   中英

包含头文件时到底发生了什么?

[英]What exactly happens when a header file is included?

我有两个疑问:

  1. 头文件实际包含什么? 所有的函数定义还是只有原型声明?
  2. 当我包含头文件时会发生什么? 是否将头文件的所有内容附加到我的代码中? 或者是否将头文件的特定(或全部)内容加载到内存中并根据我的代码调用函数?

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;
}

头文件实际包含什么? 所有的函数定义还是只有原型声明?

头文件包含函数声明,外部变量,宏,结构等。最佳做法是保持函数定义在.c文件中。

当我包含头文件时会发生什么? 是否将头文件的所有内容附加到我的代码中? 或者是否将头文件的特定(或全部)内容加载到内存中并根据我的代码调用函数?

头文件只不过是在你使用#include的地方插入它们的内容。 如果您愿意,您可以自己编写所有内容。

包含头文件等于复制头文件的内容,但我们不这样做,因为它会非常容易出错,并且在源文件中复制头文件的内容不是一个好主意,特别是我们有多个源文件,包含我们的程序。

编辑:

有关完整执行程序的内存布局,请访问此链接http://fgiasson.com/articles/memorylayout.txt

  1. 头文件应该包含函数(可能是变量)的有用声明和类型( structunionenumtypedef )和宏(类似函数或类似对象的宏)的定义。 这些通常声明某些库的某些部分提供的设施。 例如,标准头文件(例如<stdio.h> )描述了标准库的部分内容。 现代标题可能包含inline函数定义,但除此之外,标题既不应定义函数也不应定义变量 - 它应该只声明它们。

    另请注意,公共标头通常只应声明对源代码的使用者有用的类型和函数。 它应该只包括其他标题,这些标题提供了使用标题中的工具所必需的声明; 它不应该(通常)包含只有标头描述的工具实现所需的无关头。

  2. 头文件的内容(以及它包含的任何头文件)被复制到翻译单元(TU)中。 注意#define宏和条件处理( #if等)。 您通常可以找到一种方法来查看给定源文件上的预处理器的输出,以查看C编译器实际看到的内容(但要小心;它可能非常详细)。 例如,选项通常为-E (POSIX为c99命令所需)或-P (较旧的常用选项)。

TU是编译器正确看到的。 它包含文件中的源代码以及您包含的标头中的相关信息。

只是旁注,可能很明显......

  • ad 2 /如果#include文件,included文件中的#include指令将被包含的文件重新定义。 这就是发生的事情。 这就是预处理器的作用。 通过定义

  • ad 1 /头文件可以继续任何内容(编译器可以接受的任何内容)。 其他答案描述了用于在c和h文件之间组织代码的常规约定,但这些只是约定。 他们非常善于坚持,如果你不这样做,你可能会因为一个更大的项目而迷失方向。

实际上,如果你为一个非常简单的微控制器写c,你可以完全没有头文件及其包含。

如果您是学生/爱好者/有时间,可能有用的是,分别运行预处理器看看它的中间输出。 有趣的事实:很久以前我曾经使用c预处理器生成coustomized字母。

无论如何,我的观点是: c预处理器的作用是由标准定义的,同时在文件之间交换代码,这是您/您的团队可以在一定程度上选择的约定。 这种区别很重要。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM