🌞1. 头文件的概念
🌊1.1 头文件的由来
在编译器只认识.c(.cpp)文件的年代,人们发现很多.c(.cpp)文件中的声明语句是相同的,但需要重复地敲入每个文件中,导致维护困难。为了解决这个问题,人们将重复的部分提取出来放在一个新文件中,即“头文件”,通常使用.h扩展名。这些头文件包含了变量和函数的全局性声明,被其他.c文件共享,方便修改和维护大型代码。因此,头文件中通常只包含声明而很少包含具体实现的代码。
🌊1.2 头文件的作用
头文件(通常以.h
为扩展名)是用来声明变量、函数、宏等的文件,它通常包含在源代码文件中,用于向编译器提供关于程序中各种元素的信息。下面是关于头文件的一些常见理解:
- 声明和定义的分离:头文件包含了各种声明,如函数原型、变量声明、宏定义等,而对应的定义通常在其他的源代码文件中。这样做的好处是可以在程序中分离接口和实现,使得代码更具有模块化和可维护性。
- 共享代码:头文件可以被多个源代码文件引用,这样就可以在多个文件之间共享函数、变量等信息,避免了重复编写相同的代码,提高了代码的重用性。
- 编译优化:使用头文件可以让编译器在编译时对代码进行更好的优化,因为编译器可以在编译单个源文件时了解到所有需要的函数原型和变量声明,从而做出更好的优化决策。
- 可读性和维护性:头文件可以作为接口文档,向其他开发者传达关于函数、变量等的信息,提高了代码的可读性和可维护性。通过查看头文件,开发者可以快速了解程序的接口和结构。
- 预处理指令:头文件中通常包含预处理指令,如条件编译、宏定义等,用于控制代码的编译行为,实现跨平台兼容性、调试信息开关等功能。
总的来说,头文件在C/C++编程中扮演着非常重要的角色,它们不仅定义了程序的接口,还能提高代码的重用性、可读性和可维护性,是模块化编程的重要组成部分。
🌊1.3 在.h文件中实现函数也不会出错的原因
要解决上述问题,首先必须弄清编译器的工作原理。编译器的最终目的是将程序员编写的源代码转换成机器能够识别运行的二进制机器码。
大体可分成 4 个步骤:
- 头文件的预编译,预处理
编译器在编译源代码时,会先编译头文件,保证每个头文件只被编译一次。
在预处理阶段,编译器将c文件中引用的头文件中的内容全部写到c文件中。- 词法和语法分析(查错)
- 编译(汇编代码)
转化为汇编码,这种文件称为目标文件。后缀为.obj。- 链接(二进制机器码)
将汇编代码转换为机器码,生成可执行文件。后缀为.exe。
在.h文件中实现函数不会出错的原因是因为.h文件的内容在预处理阶段被直接包含到调用它的源文件中,而在编译阶段,编译器只关注源文件的内容,而不关心它是如何被包含的。因此,将函数的实现放在.h文件中并不会导致编译错误。
然而,这种做法并不是推荐的编程习惯。通常,头文件应该只包含函数的声明和数据结构的定义,而不应该包含函数的实现。
🌞2. 简单示例
🌊2.1 头文件addition.h
addition.h内容如下:
#ifndef ADDITION_H #define ADDITION_H // 函数声明 int add(int a, int b); #endif /* ADDITION_H */
补充:
头文件为什么喜欢写类似下面的内容:
#ifndef ADDITION_H #define ADDITION_H //内容。。。 #endif
这是头文件中常见的一种写法,被称为"头文件保护(Header Guard)"或"防止重复包含(Include Guard)"。它的作用是防止同一个头文件被多次包含导致的重定义错误。
#ifndef ADDITION_H
: 这是一个预处理器指令,意为 "if not defined",即如果宏ADDITION_H
没有被定义过,则执行下面的代码。这里ADDITION_H
是一个自定义的宏名称,通常会取和头文件名相关的名字。#define ADDITION_H
: 这是另一个预处理器指令,用来定义宏ADDITION_H
。这样,当ADDITION_H
被定义后,#ifndef ADDITION_H
就会失败,后面的代码将不会被包含。#endif
: 这是一个预处理器指令,用来结束条件编译块。它表示着条件编译的结束。
这种写法的目的在于,当头文件被包含多次时,只有第一次包含时会真正将文件内容包含进去,后续的包含则会被预处理器忽略,从而避免了重定义错误。
这样的预处理器指令在大型项目中尤其有用,因为一个头文件可能会被多个源文件包含,如果没有适当的保护措施,就会导致重定义错误。
🌊2.2 头文件接口实现addition.cpp
addition.cpp内容如下:
#include "addition.h" // 函数实现 int add(int a, int b) { return a + b; }
通常头文件对应接口的实现命名与头文件类似,比如addition.cpp是头文件addition.h的接口实现。
🌊2.3 源文件
源文件调用头文件addition.h
#include <stdio.h> #include "addition.h" // 包含头文件 int main() { int result = add(5, 3); // 调用add函数 printf("Result: %d\n", result); return 0; }