多文件编程
多文件编程
代码模块化
当需求比较复杂的时候或者做一个比较大的项目的时候,不可能将所有的源码都写到一个文件中,此时就需要进行模块化处理,思路如下:
- 将需求拆分成若干个小模块,每个模块对应一个源文件
- 给每个源文件提供一个头文件,通过这种方式实现函数的复用
- 头文件进行函数声明
- 源文件进行函数定义
假设我现在要做一个计算器,根据需求拆分成了加、减、乘、除四个模块,那么此时对应的源文件就是五个:
- 处理加法的源文件:
add.c - 处理减法的源文件:
subtract.c - 处理乘法的源文件:
multiply.c - 处理除法的源文件:
divide.c - 主函数
main(入口函数)对应的源文件:test.c
add.h
// add.h
#ifndef ADD_H_
// 函数声明
int add(int a, int b);
#define ADD_H_
#endif
add.c
#include "add.h"
#include <stdio.h>
int add(int a, int b)
{
return a + b;
}
subtract.h
// subtract.h
#ifndef SUBTRACT_H_
// 函数声明
int sub(int a, int b);
#define SUBTRACT_H_
#endif
subtract.c
#include "subtract.h"
#include <stdio.h>
int sub(int a, int b)
{
return a - b;
}
main.c
#include "substract.h"
#include "add.h"
#include <stdio.h>
int main()
{
int number = 9;
int number1 = 3;
int res1 = add(number, number1);
int res2 = sub(number, number1);
printf("res1 = %d, res2 = %d\n", res1, res2);
return 0;
}
关于乘法和除法的例子在这里就不写了,如法炮制就可以,完全是一样的。对于上面的代码有一下细节需要说明一下:
- 一个头文件可能被多个源文件包含,只要包含了这个头文件,在源文件中就可以使用头文件中声明的函数了
- 一个头文件中也可以包含其它头文件
- 在包含多个头文件的时候,对顺序没有要求
要避免自定义头文件被重复包含
在C语言中,一个源文件对应一个头文件并不是必须的,有时候多个源文件可以对应同一个头文件,也就是说N个源文件中定义的函数的声明都被放到了同一个头文件中。
calc.h
// add.h
#ifndef ADD_H_
// 函数声明
int add(int a, int b);
int sub(int a, int b);
#define ADD_H_
#endif
add.c
#include "add.h"
#include <stdio.h>
int add(int a, int b)
{
return a + b;
}
subtract.c
#include "subtract.h"
#include <stdio.h>
int sub(int a, int b)
{
return a - b;
}
main.c
#include "calc.h"
#include <stdio.h>
int main()
{
int number = 9;
int number1 = 3;
int res1 = add(number, number1);
int res2 = sub(number, number1);
printf("res1 = %d, res2 = %d\n", res1, res2);
return 0;
}
避免头文件重复包含
在包含头文件的时候可能由于失误将一个头文件包含了两次,或者是因为头文件的嵌套包含导致某个头文件被包含了多次(>1)
// a.h
#include "hello.h"
#include <stdio.h>
#include "hello.h" // 头文件被重复包含
// b.h
#include "a.h"
// c.h
#include "a.h"
#include "b.h" // 头文件展开之后发现 a.h 在 c.h 中包含了两次
如果出现了以上情况,程序在编译的时候就会报错。为了避免同一个文件被include多次,C/C++中有两种方式:
-
使用文件保护宏
文件保护宏(header guard macro)是一种
传统的预处理指令,用于防止头文件重复包含。文件保护宏可以确保在编译时只包含一次特定的头文件,从而避免由于多次包含导致的重复定义错误。文件保护宏需要在头文件的开头和结尾进行定义和结束,如下所示:#ifndef HEADER_FILE_NAME_H #define HEADER_FILE_NAME_H // 头文件的内容 #endif // HEADER_FILE_NAME_HHEADER_FILE_NAME_H是一个唯一的标识符,用于确保宏定义的唯一性,因此每个头文件都应该使用不同的宏名称。- 可以根据需要自定义宏的名称,常见的做法是
以头文件名称的大写形式作为宏的名称。 - 在编译过程中,首次包含头文件时,
ifndef条件为真,进入#define块,并定义了该宏。随后的重复包含,由于宏已经有定义,条件为假,不再执行#define块内的代码。
-
使用
#pragma once#pragma once是一种预处理指令,用于确保头文件只被编译一次。它是一种用于防止头文件重复包含的非标准的预处理指令,被大多数主流编译器所支持。使用
#pragma once可以替代传统的头文件保护宏,如#ifndef、#define和#endif。通过使用#pragma once,可以更简洁地确保头文件只被编译一次,而无需手动编写宏定义。示例使用
#pragma once的头文件:#pragma once // 头文件内容需要注意的是,虽然
#pragma once在大多数情况下提供了简洁的头文件包含机制,而且几乎所有主流的编译器都支持它,但它不是C或C++的标准预处理指令。因此,如果您希望代码更具可移植性,可以继续使用传统的头文件保护宏。

浙公网安备 33010602011771号