返回:安全开发篇(一)目录 · 上一篇:005、注释方式 · 下一篇:007、程序入口点

对应视频:1.5 基础-#include预处理指令.mp4

1、学习目标

这一节整理 #include。它是 C/C++ 里非常常见的预处理指令,用来在编译前引入头文件内容。

需要掌握:

  1. #include 的基本作用;
  2. 尖括号和双引号的区别;
  3. 头文件通常放什么;
  4. 它和编译流程的关系;
  5. 安全开发中为什么要关注头文件和声明。

2、基本写法

1
2
3
4
5
6
7
#include <stdio.h>

int main(void)
{
printf("Hello\n");
return 0;
}

stdio.h 是标准输入输出头文件。使用 printf 前包含它,可以让编译器知道 printf 的声明。

#include 是预处理指令,不是普通 C 语句,所以结尾不写分号。

3、尖括号和双引号

写法 查找方式 常见用途
#include <xxx.h> 优先从系统头文件目录查找 标准库、系统 SDK、第三方库
#include "xxx.h" 优先从当前工程或当前文件所在目录查找,找不到再查系统目录 自己写的头文件

示例:

1
2
#include <stdio.h>
#include "calc.h"

stdio.h 是系统标准库头文件,calc.h 可以是自己项目里的函数声明文件。

4、预处理流程

可以先粗略理解为:

  1. 编译前,预处理器扫描 # 开头的指令;
  2. 遇到 #include,把对应头文件内容纳入当前编译单元;
  3. 处理宏定义和条件编译;
  4. 编译器再正式分析 C 语法。

头文件本身通常不直接生成可执行程序,它更多是在编译时提供声明和公共定义。

5、头文件通常放什么

内容 示例 说明
函数声明 int add(int a, int b); 告诉编译器函数形式
结构体声明 struct User { ... }; 统一数据结构
宏定义 #define MAX_SIZE 128 定义常量或简单替换
类型别名 typedef unsigned int uint32; 简化类型表达
外部变量声明 extern int g_count; 声明其他文件中的变量

初学时可以先把头文件理解成“公共说明书”。

6、代码实验

calc.h:

1
2
3
4
5
6
#ifndef CALC_H
#define CALC_H

int add(int left, int right);

#endif

calc.c:

1
2
3
4
5
6
#include "calc.h"

int add(int left, int right)
{
return left + right;
}

main.c:

1
2
3
4
5
6
7
8
#include <stdio.h>
#include "calc.h"

int main(void)
{
printf("%d\n", add(3, 5));
return 0;
}

这里 main.c 不需要知道 add 的完整实现,只需要通过 calc.h 知道它的声明。

7、安全开发观察

头文件和安全开发的关系主要体现在:

  1. 调用系统 API 前,需要对应头文件提供声明;
  2. 函数声明不匹配可能造成参数解释错误;
  3. 结构体声明决定内存布局理解;
  4. 宏定义可能影响编译出来的代码路径;
  5. 多文件工程中,声明和实现分离能帮助分析模块边界。

逆向或代码审计时,头文件能帮助快速理解函数原型、结构体字段和常量含义。

8、复盘题

8.1 #include <stdio.h> 的作用是什么?

它在编译前引入标准输入输出头文件,让编译器知道 printf、scanf 等函数的声明。

8.2 尖括号和双引号有什么区别?

尖括号通常用于系统或库头文件,优先从系统目录查找;双引号通常用于项目自己的头文件,优先从当前工程目录查找。

8.3 为什么头文件里常见 #ifndef、#define、#endif?

这是头文件保护,避免同一个头文件被重复包含后造成重复定义或编译冲突。

这一节要记住:#include 发生在编译前,它解决的是“让当前源文件知道外部声明”的问题。