C 语言书
模块三 · 进阶与实战 / 第 15 课

15 · 预处理与多文件组织

进阶 35 分钟 跳到过关题
学习目标
  • 会用 #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 的作用是?

必做作业
  1. 给自己的工具函数写一个 util.h / util.c。
  2. 用 #define DEBUG 0/1 控制是否打印调试信息。
  3. 解释:为什么头文件里通常只放声明,不放函数体?
先自己写完,再点开看解答

1:h 里放声明和卫士,c 里放实现,main 只 include h。

2:函数里用 #if DEBUG 包住 printf,改宏即可关掉日志。

3:函数体放 h 且被多个 c 包含,会重复定义。声明可以多处出现,定义只能有一处。