我有两个疑问:
- 头文件实际上包含什么?所有函数定义还是只有原型声明?
- 当我包含一个头文件时会发生什么?头文件的所有内容都会附加到我的代码中吗?还是特定(或全部)头文件的内容被加载到内存中,根据我的代码调用函数?
我有两个疑问:
C和C++是编程语言,它们广泛使用名为函数转发的特性。这意味着您可以这样说:
void f(int i); /* note the semicolon */
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。
struct
、union
、enum
或typedef
)和宏定义(函数式或对象式宏)。这些通常声明了某个库的一部分提供的功能。例如,标准头文件(如<stdio.h>
)描述了标准库的一部分。现代头文件可能包含inline
函数定义,但除此之外,头文件不应定义任何函数或变量,只应声明它们。#define
宏和条件处理(如#if
等)会被记录下来。通常可以找到一种方法来查看给定源文件的预处理器输出,以查看C编译器实际看到的内容(但要小心,它可能非常冗长)。例如,选项通常是-E
(由POSIX命令c99
要求)或-P
(一个较旧的常用选项)。只是一点小提醒,可能很明显......
广告2/ 如果您#include一个文件,则包含文件中的#include指令将被所包含的文件替换。 这就是发生的事情。 这是预处理器执行的操作。 根据定义
广告1/ 头文件可以继续任何内容(编译器可以接受的任何内容)。 其他答案描述了用于在c和h文件之间组织代码的常规约定,但这些仅是约定。 如果不遵循它们,您很可能会在更大的项目中迷失方向。
实际上,如果您为非常简单的微控制器编写c代码,则完全可以没有头文件及其包含。
如果您是学生/爱好者/有时间,则运行预处理器并查看中间输出可能很有用。 有趣的事实:很久以前我曾使用c预处理器生成过定制信件。
无论如何,我的观点是:c预处理器的作用由标准定义,而文件之间的代码组织是一种您/您的团队在一定程度上可以选择的约定。这种区别非常重要。