参考:https://blog.csdn.net/sksukai/article/details/10561187

总结

说明:出现这个错误,请你先检查重复定义的变量是否是定义在了.h头文件中,如果是,请您耐心的看完这篇文章,他会告诉你错误的根本原因。

如果你很着急,不想弄清楚原因,请直接按下面的方法更改:

假设重复定义的变量是int a,且你定义在了b.h,想作为全局变量使用,那么:

  • 1.删除b.h中的int a
  • 2.在b.cpp中加入a的定义int a;
  • 3.在b.h中加入 extern int a;
  • 4.在要使用a的cpp文件中加入#include “b.h”

1.引言

学习计算机那么久了,也编程这么久了,但是一直都没有彻底弄清楚头文件互相包含时,为什么有时候会出错,出现重复定义,有时候确又能够正常编译链接,今天仔细研究了一下这个问题,将结论记录下来。

2.编译

编译,大家都知道,最后就会产生一个.o文件,我以前估计就理解到这个水平。于是我常常想不通一个问题,比如MFC的一个大工程,里面有a.cpp,a.h,b.cpp,b.h,那他们是合在一起编译吗?那互相包含会不会出错?

结论:编译是针对一个文件来说的,比如有a.cpp,a.h,b.cpp,b.h,他们之间的编译是没有关系的,a.cpp和a.h会产生一个a.o,他的编译和b完全没有关系,同样b.cpp和b.h产生的b.o和a也没有关系(除了include包含的情况)。

3.链接

链接就很有可能是很多.o文件一起链接,组成一个可执行文件,例如g++ a.cpp b.cpp -o c,这样就是先单独编译了a.cpp和b.cpp并将产生的a.o,b.o链接为c

4.头文件包含的问题

我们都知道,ifndef是为了防止头文件重复包含,比如

a.h:

#include "b.h"
#include "c.h"

b.h:

#include "c.h"

c.h:

//内容......

这样的话,我们编译a.cpp肯定会出现问题,因为a.h里面包含了2次c.h,而c.h里面没有加入#ifndef之类的语句

所以我们需要将c.h改为下面的形式:

c.h

#ifndef _C_HEADER
#define _C_HEADER

//.....内容

#endif

这样的话,编译a.cpp就没错了,但是我们要注意,#ifndef只是针对某一个文件,而不是一个工程,比如你在a.h里面包含了c.h,在b.h里面也包含了c.h,这样不管你的c.h有没有加入#ifndef,你的c.h文件在a.h和b.h当中都会展开,而不是说a和b因为是一个工程,所以只展开一次。

5.extern的用法

首先,头文件.h中不适宜定义变量,我们都知道定义全局变量的常规使用方法是在.cpp文件中定义变量,在.h文件中用extern申明,这是为什么呢?看下面的例子:

比如在MFC的一个大工程中,我想定义一个全局变量int a,因为所有的文件都包含stdafx.h,我们考虑写在里面

第一种写法:

stdafx.h:

int a
//........

a.cpp

#include "stdafx.h"

extern int a;
a = 100;
//.....

b.cpp

#include "stdafx.h"

extern int a;
a = 200;
//.......

接下来,

编译,没错耶!!!好开心

链接,怎么出错了?!!!重复定义a?好难过…

我就是这么难过…终于弄除了原因:

原因:在编译的时候,由于是一个一个文件为单位,所以a.cpp a.h被单独编译产生a.o,这样在编译的时候,由于他包含了stdafx.h,所以在a.o中,已经有int a的定义了

而b.cpp b.h也是被单独编译的,产生b.o里面也有一个int a的定义,所以出现了重复定义的错误。

正确的写法

stdafx.cpp

int a;

stdafx.h

extern int a;

a.cpp

#include"stdafx.h"

a=100;
//......

b.cpp


#include"stdafx.h"
//......
a= 200;

这样会发现编译链接都正确,这是为什么呢?

因为在编译的时候,由于mfc会自己默认编译stdafx.h和stdafx.cpp产生stdafx.o文件,然后我们的a.cpp编译的时候包括了stdafx.h,但是stdafx.h里面只有extern a,这就说明,我们在下面使用到的a都是在其他文件中定义的,同样的b.cpp编译时,也不会定义a,所以编译出来的a.o和b.o都不包含a的定义。最后链接,链接的时候,由于这是一个工程,所以mfc会把stdafx.o,a.o,b.o链接在一起,这样我们的a就定义了一次,在stdafx.o中。

看到这里,你可能会觉得奇怪,我都不知道我按下vc6.0的编译按钮,是编译了那么多文件,按下链接按钮,又是把所有文件链接到一起了,这些可能需要进一步了解makefile之类的东西。

6.结论

  • 1.编译是针对一个一个文件来说的,而链接则是针对一个工程所有的.o文件而言的。
  • 2.#ifndef只是对防止一个文件的重复编译有效
  • 3.全局变量最好在.cpp文件中定义,在.h文件中加上extern申明,因为在.h文件中定义,容易在链接时造成变量重定义。
  • 4.注意mfc中会自动编译stdafx,并将stdafx.o加入到工程中一起链接.