欢迎光临
我们一直在努力

【一篇即毕业系列】C++的头文件 & 源文件 从基础到通天!!

文章目录

  • 头文件&源文件
    • 基本概念
      • 头文件(.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 等,用于宽字符的类型判断和大小写转换。
赞(0)
未经允许不得转载:171主机测评 » 【一篇即毕业系列】C++的头文件 & 源文件 从基础到通天!!
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址