模块三 · 进阶与实战 / 第 15 课
15 · 预处理与多文件组织
学习目标
- 会用 #ifndef 防止头文件重复包含
- 会把声明放 .h、实现放 .c
- 会用 #ifdef 做调试开关
头文件卫士
/* util.h */
#ifndef UTIL_H
#define UTIL_H
int add(int a, int b);
#endif
拆成两个文件
/* util.c */
int add(int a, int b)
{
return a + b;
}
/* main.c */
#include <stdio.h>
#include "util.h"
int main(void)
{
printf("%d\n", add(2, 3));
return 0;
}
调试开关
#define DEBUG 1
void log_msg(const char *s)
{
#if DEBUG
printf("[LOG] %s\n", s);
#else
(void)s;
#endif
}
include 两种写法
#include <stdio.h>:系统库,用尖括号#include "util.h":自己的头文件,用引号
多文件工程怎么组织(建议照做)
| 文件 | 放什么 | 对外暴露 |
|---|---|---|
main.c | 初始化 + 主流程 | 无(或只有 main) |
util.h / util.c | 通用工具函数 | 函数原型 |
config.h | 常量、宏、开关 | 只被 include |
原则:声明进 .h,实现进 .c;main.c 只关心「调用」,不关心「怎么实现」。
/* ---------- config.h ---------- */
#ifndef CONFIG_H
#define CONFIG_H
#define MAXN 100
#define DEBUG 1
#endif
/* ---------- util.h ---------- */
#ifndef UTIL_H
#define UTIL_H
int max2(int a, int b);
#endif
/* ---------- util.c ---------- */
#include "util.h"
int max2(int a, int b)
{
return a > b ? a : b;
}
/* ---------- main.c ---------- */
#include <stdio.h>
#include "util.h"
#include "config.h"
int main(void)
{
printf("%d\n", max2(3, 9));
#if DEBUG
printf("调试信息:MAXN=%d\n", MAXN);
#endif
return 0;
}
本课过关
必做#ifndef UTIL_H 的作用是?
逐步解析
预处理读到 #include "util.h" 时会把头文件内容插进来。若 util.h 被 include 两次(直接或间接),里面的声明/定义可能出现两遍,轻则警告,重则「重定义」错误。卫士写法:第一次进来时 UTIL_H 未定义,于是 #define UTIL_H 并放入内容;第二次再进来时 #ifndef 为假,整段被跳过。几乎所有 .h 开头都应有这一对 #ifndef/#define,结尾 #endif。这和「会不会跑错」无关,是为了让多次包含安全。
必做作业
- 给自己的工具函数写一个 util.h / util.c。
- 用 #define DEBUG 0/1 控制是否打印调试信息。
- 解释:为什么头文件里通常只放声明,不放函数体?
先自己写完,再点开看解答
1:h 里放声明和卫士,c 里放实现,main 只 include h。
2:函数里用 #if DEBUG 包住 printf,改宏即可关掉日志。
3:函数体放 h 且被多个 c 包含,会重复定义。声明可以多处出现,定义只能有一处。
学完去哪
最后一课综合项目,把模块三串起来。