文章目录
- 头文件&源文件
-
- 基本概念
-
- 头文件(.h 或 .hpp):
- 源文件(.c 或 .cpp 或 .cc):
- #include 指令:
- 示例代码
-
- 头文件(math_utils.h):
- 源文件(math_utils.cpp):
- 主程序文件(main.cpp):
- 模块化编程
- 头文件重复包含问题我们需要重点关注
- 编译链接过程
-
- 编译:
- 链接:
- 头文件在编译链接过程中的作用:
- 命名规范
-
- 示例命名规范:
- C常用标准库头文件总结
头文件&源文件
程序喵 厨子 | 2025年3月9日修改
在 C/C++ 项目中,头文件(.h 或 .hpp)和源文件(.c 或 .cpp 或.cc)扮演着至关重要的角色。
基本概念
头文件(.h 或 .hpp):
- 作用:头文件通常用于声明函数、变量、类型定义(如结构体、类)、宏定义和常量等。有了这些声明,其他文件能够引用这些符号,不需要了解它们的具体实现细节。
- 特点:头文件不应包含任何实际的代码实现(也就是函数体),除非这些代码是内联函数或模板定义或 static 函数(好多除非…)。
源文件(.c 或 .cpp 或 .cc):
- 作用:源文件包含函数的实际实现、全局变量的定义以及程序的入口点(比如 main 函数)。
- 特点:源文件是编译器生成目标文件的基本单位。
#include 指令:
- 作用:#include 用于将指定的头文件内容插入到包含该指令的源文件中。有了 #include,源文件可访问头文件中声明的符号。
- 预处理器处理:在编译之前,预处理器会查找并替换所有的 #include 指令,将头文件的内容插入到相应的位置,像复制粘贴一样。
示例代码
头文件(math_utils.h):
#ifndef MATH_UTILS_H
#define MATH_UTILS_H
// 函数声明
int add(int a, int b);
int subtract(int a, int b);
#endif // MATH_UTILS_H
源文件(math_utils.cpp):
#include "math_utils.h"
// 函数实现
int add(int a, int b) {
return a + b;
}
int subtract(int a, int b) {
return a – b;
}
主程序文件(main.cpp):
#include <iostream>
#include "math_utils.h"
int main() {
int x = 5, y = 3;
std::cout << "Sum: " << add(x, y) << std::endl;
std::cout << "Difference: " << subtract(x, y) << std::endl;
return 0;
}
模块化编程
通过头文件和源文件的分离,可以实现模块化编程,提高代码的可读性。
- 头文件定义了模块的接口,提供了模块对外暴露的功能和数据结构。其他模块通过包含头文件来访问这些接口,而无需关心模块的具体实现细节。
- 源文件实现了头文件中声明的接口,提供了模块的具体功能实现。源文件是模块的内部实现部分,通常不对外公开。
头文件重复包含问题我们需要重点关注
- 问题:如果头文件被多次包含,可能会导致重复声明错误。
- 解决方案:使用头文件保护符(header guards),即 #ifndef、#define 和 #endif 指令,确保头文件的内容只被包含一次。或者使用 #pragma once,个人更推荐 #pragma once,推荐阅读 本系列中的 #ifndef vs #pragma once
编译链接过程
编译:
- 编译器将每个源文件转换为目标文件(.o 或 .obj),目标文件包含程序的机器代码,但还尚未链接到其他目标文件或库。
- 头文件在编译过程中会被预处理器包含到源文件中,但不会被直接编译成目标文件。(源文件才是编译的基本单位)
链接:
- 链接器将多个目标文件和库链接成一个可执行文件或库。
- 链接器解析所有外部符号引用,确保每个引用都有对应的定义。
头文件在编译链接过程中的作用:
- 提供声明,使链接器能够找到正确的符号定义。
- 允许多个源文件共享相同的声明,而无需重复代码。
命名规范
下面是一些常见的命名约定:
- 头文件:使用 .h 或 .hpp 后缀,通常命名与源文件相对应,但去掉 .cpp 部分。例如,math_utils.cpp 的头文件是 math_utils.h。
- 源文件:使用 .c 或 .cpp 或 .cc 后缀,命名应反映其包含的内容或功能,我个人更多的会用 .cc 后缀。
- 命名风格:驼峰命名法或下划线分隔法,哪种风格都可,但整个项目用一致,我个人更多的用下划线分割法。
示例命名规范:
- 头文件:math_utils.h,data_structures.hpp
- 源文件:math_utils.cpp,data_structures.cc
C常用标准库头文件总结
见下表:
| stdio.h | 提供输入输出函数,如 printf、scanf、fopen、fclose 等,用于文件操作和格式化输入输出。 |
| stdlib.h | 提供常用的函数,如内存分配(malloc、free)、随机数生成(rand)、字符串转换(atoi、itoa)、程序控制(exit、abort)等。 |
| string.h | 提供字符串处理函数,如 strlen、strcpy、strcmp、strcat、strstr 等,用于字符串的复制、比较、查找等操作。 |
| math.h | 提供数学函数,如 sin、cos、tan、sqrt、exp、log 等,用于各种数学运算。 |
| ctype.h | 提供字符处理函数,如 isalnum、isalpha、isdigit、tolower、toupper 等,用于字符类型的判断和大小写转换。 |
| time.h | 提供日期和时间处理函数,如 time、localtime、gmtime、difftime、strftime 等,用于获取和格式化时间。 |
| stdarg.h | 提供处理可变参数列表的函数,如 va_start、va_arg、va_end,用于实现可变参数函数。 |
| signal.h | 提供信号处理函数,如 signal、raise,用于设置和处理信号。 |
| assert.h | 提供断言宏 assert,用于在调试时检查条件表达式,帮助发现程序中的错误。 |
| setjmp.h | 提供非局部跳转函数 setjmp 和 longjmp,用于在程序的不同部分之间跳转。 |
| errno.h | 定义错误代码变量 errno,用于表示程序运行过程中发生的错误。 |
| stddef.h | 提供标准库的一些常用定义,如 size_t、ptrdiff_t、wchar_t、NULL、offsetof 等。 |
| locale.h | 提供本地化函数,如 setlocale、localeconv,用于处理与地域相关的设置。 |
| float.h | 提供与浮点类型相关的一些常量和定义,如 FLT_MAX、DBL_MAX、LDBL_MAX,用于描述浮点类型的特性和限制。 |
| limits.h | 提供与整型类型相关的常量和宏,如 INT_MAX、INT_MIN、CHAR_BIT,用于获取整型类型的范围信息。 |
| stdbool.h | 提供布尔类型和布尔常量 true、false,用于在C语言中更方便地使用布尔类型的变量和常量。 |
| stdint.h | 提供固定宽度的整数类型,如 int8_t、int16_t、int32_t、uint64_t 等,用于跨平台编程中确保整数类型的大小和行为的一致性。 |
| tgmath.h | 提供一种泛型的数学函数定义,根据参数的类型自动选择合适的函数版本进行调用。 |
| wchar.h | 提供宽字符处理函数,如 wcslen、wcscpy、wctomb 等,用于宽字符字符串的处理。 |
| wctype.h | 提供用于分类宽字符的函数,如 iswalpha、iswdigit、towlower、towupper 等,用于宽字符的类型判断和大小写转换。 |



