浅析C/C++作用域之全局变量
来源:互联网 发布:同志软件有那些 编辑:程序博客网 时间:2024/06/06 12:26
楼主今天遇到一件很蛋疼的事,写了不到30行代码居然出错,尼玛气人的是半天找不到原因,真是少壮不努力老大徒伤悲啊!
废话少说,先看代码:
(IDE=vs2010)
Head.h
#ifndef HEAD_H //编译预处理,避免多次包含#define HEAD_H struct Node{int data;};Node Dt; //定义结构体变量。void setData(int dt);#endif;
Head.cpp
#include"Head.h"void setData(int dt){Dt.data=dt;}
Main.cpp
#include<iostream>#include"Head.h"using namespace std;int main(){int input=1000;setData(input);cout<<Dt.data;return 0;}编译时,错误提示:
Main.obj : error LNK2005: "struct Node Dt" (?Dt@@3UNode@@A) 已经在 Head.obj 中定义
d:\document_x64\documents\visual studio 2010\Projects\Test\Debug\Test.exe : fatal error LNK1169: 找到一个或多个多重定义的符号
哎呀,没道理啊,怎么会重定义呢,编译器大哥你抽风了吧!仔细想想估计还是自己的问题。结果在多方求助的情况下进行进一步尝试:
分析认为:Node Dt; //定义结构体变量。此变量要在其他文件使用必须声明为extern类型,即extern Dt; Head.h#ifndef HEAD_H //编译预处理,避免多次包含#define HEAD_H struct Node{int data;};Node Dt; //定义结构体变量。void setData(int dt);#endif;
Head.cpp
#include"Head.h"void setData(int dt){Dt.data=dt;}
Main.cpp
#include<iostream>#include"Head.h"using namespace std;
extern Dt;//语法错误int main(){int input=1000;setData(input);cout<<Dt.data;return 0;}
哎呀,怎么回事,直接语法错误,纠结。查书一看,貌似extern 用法是在不用include文件时需要使用的。
一计不成,再生一计:直接在Head.h里面声明为extern Node Dt,这回该成了吧。
Head.h
#ifndef HEAD_H //编译预处理,避免多次包含#define HEAD_H struct Node{int data;};extern Node Dt; //定义结构体变量。void setData(int dt);#endif;
Head.cpp
#include"Head.h"void setData(int dt){Dt.data=dt;}
Main.cpp
#include<iostream>#include"Head.h"using namespace std;int main(){int input=1000;setData(input);cout<<Dt.data;return 0;}
这回更叫人郁闷了:编译器提示找不到定义的标识符Dt。这叫老夫如何是好啊。
仔细想想最开始就没错啊,为什么会出现重定义呢:
#ifndef HEAD_H
#define HEAD_H
代码段
#endif;
这个不是可以避免重复定义吗?怎么会,我又一次怀疑编译器大哥了。后来纠结中,经高人点化终于明白了是怎么回事,原来上述代码段只能保证一个文件中不能多次包含同一个文件,并不能保证多个文件不能多次包含并编译该文件。
这个其实说完我自己也不理解,举个例子吧:
我们有文件:A,B,C
B包含(include)A ; C包含B,A;那么在C中就会出现两次包含A,若没有上面的预处理就会导致同一文件中的重定义事件发生。
然而B中包含A,C中包含A,这样会导致A会编译两次,若其中有定义语句,那么也会被定义两次,这就会出现全局变量在不同文件中重定义事件发生。
例如:
A.h
int i;
B.cpp
#include"A.h"
cout<<i;
C.cpp
#include"A.h"
#include"B.h"
cout<<i;
事实上B.cpp也可以写成:
int i; //#include就是把代码导入
cout<<i;
同理C.cpp也可写为:(这里为思路更加清晰分两个阶段,编译器具体怎么操作就不知道了)
第一阶段:导入B.h A.h
int i;
#include"A.h"
cout<<i;
cout<<i;
第二阶段:导入从B.h中导入的A.h
int i;
int i; //很显然发生了重定义
cout<<i;
cout<<i;这就是最终的C.cpp
如果加入预处理模块:那么C.cpp会是:
int i;
cout<<i;
cout<<i;但是由于全局变量的默认可见性是external(外部),所以在同一个工程中A.h,B.h,C.h都执行了int i;这样i被定义了三次,出现了全局变量在不同文件中的重定义。
好的至此我们依然解开了#include的面纱,也明白了错误之所在。
解决办法是,我们在A.h中不是定义全局变量,而是声明(我们也应想到,函数也不应在.h中定义,而只是声明)。改进后A为
A.h
extern int i; //extern声明了一个外部可见且可用的变量。
注:即使加了extern在现代编译器中也必须包含文件A才可以使用,因为现代编译器是先对单个文件编译的,编译时各文件间是透明的。
这样问题就解决了,下面贴下正确代码:
Head.h
#ifndef HEAD_H //编译预处理,避免多次包含#define HEAD_H struct Node{int data;};extern Node Dt; //定义结构体变量。void setData(int dt);#endif;
Head.cpp
#include"Head.h"Node Dt;void setData(int dt){Dt.data=dt;}
Main.cpp
#include<iostream>#include"Head.h"using namespace std;int main(){int input=1000;setData(input);cout<<Dt.data;return 0;}输出结果:1000
最后补充一句:
extern Node Dt;
换成
static Node Dt;
也可以。
因为static只会在最开始声明的地方定义,也仅仅定义这一次,所以不会出现重定义。
总结篇
代码段AB.h
#include“A.h”
代码段B等价B.h
代码段A
代码段B正因为这样,可能会出现多个文件包含同一个文件I,若被包含的文件I中有全局变量的定义(而全局变量是外部可见的),则会导致同一变量在不同文件的多次定义。
#define 标识符
//代码段
#endif;
这段代码的作用是:保证一个文件中不会多次包含同一个文件(即不会多次导入同一个文件代码,这也就避免了同文件中的重定义问题)
注意:该代码段中的标识符只在文件中有效,不同文件互不影响,如A中定义可Head_H不代表B中定义了Head_H(因为现代编译器是静态编译,是以文件为单位,不同文件间编译是透明的)。
(3)全局变量的可见性:全局变量在默认情况下是external(外部)可见,故应该尽量少使用全局变量,否则很容易出现命名冲突,而导致重定义错误。如果非得使用全局变量则必须在头文件中声明,然后在某一个cpp文件中定义(记住只在一个cpp中定义),然后在其他需要的地方使用。编程风格应该尽量保持一个风格:.cpp文件只包含.h。最好不要.h包含.h,.h包含.cpp;.cpp包含.cpp。
- 浅析C/C++作用域之全局变量
- c语言之static全局变量,变量作用域
- linux c作用域 全局变量使用
- C++的局部变量和全局变量的作用域
- C/C++全局变量和局部变量作用域问题
- 浅析extern “C”的作用
- 浅析extern “C”的作用
- 浅析extern “C”的作用
- C语言extern作用(全局变量)
- C语言extern作用(全局变量)
- C语言extern作用(全局变量)
- C语言之作用域
- C ++之继承浅析
- C全局变量
- [C++]全局变量
- C/C++中全局变量和全局文件的访问作用域
- C/C++语言中变量作用域:局部变量,全局变量,文件级变量
- C-052.static与extern对全局变量的作用
- 71道经典Android面试题和答案,重要知识点都包含了
- Codeforces Round #143 (Div. 2) A题
- springMVC返回hibernate多对多对象的json时 出现无限循环
- Android开发中使用Spinner控件出现的问题
- DBCP和JDBC学习总结(应用篇)
- 浅析C/C++作用域之全局变量
- SVN服务器的配置
- Undefined symbols for architecture armv7s
- ubuntu下安装mysql
- vc6:unexpected end of file while looking for precompiled header directive
- Reason: image not found
- 人需要有意识的培养自己的能力
- Titanium Mobile 在线教学-Tidevcn.com
- nat路由与路由