昨天某鱼尝试把自己写好的整合版大作业拆成分模块版,却遇到了一些问题。我一看,发现她没有使用#ifndef
等宏定义避免对头文件的重复包含。这里是一些详细的解释。
1.问题
为了方便测试,我新建了一个项目,该项目最初包含三个文件:
- main.c
- a.c
- a.h
三个文件内容如下:
main.c
#include <stdio.h>
#include <stdlib.h>
#include "a.h"
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
int main(int argc, char *argv[]) {
printf("in main.c !\n");
test(); //在a.h声明,在a.c定义
printf("N = %d\n",N); //420
printf("a = %d\n",a); //608
printf("b = %d\n\n",b); //728
return 0;
}
a.c
#include <stdio.h>
void test(void)
{
printf("test()!\n");
}
a.h
#define N 420
int a = 608;
int b = 728;
void test(void);
a.h是头文件,其中存放了常量N
与变量a
、b
的定义以及test
函数的声明。a.c文件则存放了test函数的定义。
对项目进行编译运行,没有遇到什么问题,输出结果如下
in main.c !
test()!
N = 420
a = 608
b = 728
接着,我们向项目中添加一个头文件b.h
#include "a.h"
#define t(x) test(x)
并在main.c中加一行:#include "b.h"
:
#include <stdio.h>
#include <stdlib.h>
#include "a.h"
#include "b.h"
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
int main(int argc, char *argv[]) {
printf("in main.c !\n");
test(); //在a.h声明,在a.c定义
printf("N = %d\n",N); //420
printf("a = %d\n",a); //608
printf("b = %d\n\n",b); //728
return 0;
}
看起来似乎没什么问题。但重新编译运行,报错:
根据编译器的提示信息,error在于对变量a
进行了重复定义。
2.分析
接下来我们分析一下问题产生的原因。
首先来回忆一下#include
的用法。这是一条预处理指令,而编译之前预处理器会首先对文件进行扫描, 当发现#include
指令后,就会寻找指令后面<>
或""
中的文件名,并把这个文件的内容包含到当前文件中。被包含文件中的文本将替换源代码文件中的#include
指令, 就像你把被包含文件中的全部内容键入到源文件中的这个位置一样。
在main.c文件中,我们同时使用了以下两条指令:
#include "a.h"
#include "b.h"
也就是说,我们同时将a.h与b.h的全部内容嵌入到了main.c的这个位置。但是,在b.h文件中,也包含一条预处理指令:
#include "a.h"
这条指令再次将a.h文件中的全部内容嵌入到了main.c的这个位置,相当于在main.c中嵌入了两次a.h。而a.h文件中包含对变量a的定义:
int a = 608;
于是,a
就被重复定义了两次,引起报错。
3.解决(使用#ifndef结构)
解决方法很简单,使用#ifndef结构(Macro Guard)
#ifndef <标识>
#define <标识>
......
#endif
<标识>
在理论上来说可以是自由命名的,但每个头文件的这个“标识”都应该是唯一的。标识的命名规则一般是头文件名全大写,前后加下划线,并把文件名中的”.”也变成下划线,如: a.h
#ifndef _A_H_
#define _A_H_
......
#endif
在被包含过一次之后,宏_A_H_
已经有了,下次再碰到就会略过从#define _A_H_
开始到#endif
之间的代码。
修改后的main.c文件:
#include <stdio.h>
#include <stdlib.h>
#ifndef _A_H_
#define _A_H_
#include "a.h"
#endif
#ifndef _B_H_
#define _B_H_
#include "b.h"
#endif
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
int main(int argc, char *argv[]) {
printf("in main.c !\n");
test(); //在a.h声明,在a.c定义
printf("N = %d\n",N); //420
printf("a = %d\n",a); //608
printf("b = %d\n\n",b); //728
return 0;
}
修改后的b.h文件:
#ifndef _A_H_
#define _A_H_
#include "a.h"
#endif
#define t(x) test(x)
此时重新编译运行,不再报错,输出如下:
in main.c !
test()!
N = 420
a = 608
b = 728