全局变量定义在头文件中

2015-08-12 19:27:08来源:作者:人点击

全局变量定义在头文件中,有需要的朋友可以参考下。


最近在学习uC/OS操作系统,对其中定义的全局变量产生了好奇。作者将变量定义在头文件uCOS_II.H中,比如:
OS_EXT INT8U OSIntNesting; /* Interrupt nesting level */
OS_EXT INT8U OSIntExitY;


OS_EXT INT8U OSLockNesting; /* Multitasking lock nesting level */


OS_EXT INT8U OSPrioCur;/* Priority of current task*/
OS_EXT INT8U OSPrioHighRdy; /* Priority of highest priority task */


我们大家都知道C语言的全局变量的作用域是整个源程序,只能定义一次,如果其它文件要引用某个文件中的全局变量,那么可以用关键字extern在本文件中来声明该变量,即可使用该变量。我们也知道变量的声明或是函数的声明是可以并且一般是放在该源文件所对应的头文件中的,那么对于定义可不可以呢?在实际的编程中我们很少这样去在一个头文件中定义一个全局变量,应为如果其它文件要使用这个变量的时候,就要用include来包含这个头文件,从而去引用这个变量,我们同样知道C编译器在原文件中编译到include这条语句的时候,是将对应的头文件内容拷贝到该处的,也就是说在头文件中定义的全局变量,其实还是定义到了该源文件中。那么如果另外一个源文件也想引用这个全局变量怎么办呢?是不是同理用include包含该头文件就可以了?如果你这样做了,编译器在编译的时候是不会报错的,因为你的程序语法没有任何错误,但是在连接(link...)的时候错误就来了。在VC++6.0环境中会报出:error LNK2001: unresolved external symbol "int a" (?a@@3HA)这样的错误。这是为什么呢?每一个源文件编译之后都会产生一个OBJ或O文件(我们叫它目标文件),之前说过源文件中的include 相当于将该头文件中的所有语句放在该处,全局变量也就相应的添加进来,问题就在这里,既然全局变量被相应包含进来,两个原文件中就都分别定义了该变量,但是前面也说过,全局变量的作用域是整个源程序,只能有定义一次,然而在这个源程序中该变量被定义了两次,当然要报错了。怎么解决呢?这里有两种解决办法可供参考。


1、首先将该全局变量定义在源文件中,假设文件名为global.c,然后再创建一个头文件global.h,用extern关键字声明该变量,当然文件要使用条件编译语句#ifndef _XX_H_ #define _XX_H_ .......变量声明...... #endif 。其他文件如果要引用该变量时,直接用Include包含global.h即可。


2、这种方法思想来自于uc/os,它“违背”了这种一般原则,将全局变量定义在了头文件中。直接上源程序!


/* file 1-----------------------------------------------OS_uCOS_II.h */


#ifndef OS_uCOS_II_H
#define OS_uCOS_II_H


#ifdef OS_GLOBALS
#define OS_EXT
#else
#define OS_EXT extern
#endif


OS_EXT int g_Var;
#endif


/* file 2---------------------------------------------------------OS_uCOS_II.c*/


#define OS_GLOBALS


#include "uCOS_II.h"


/* file3------------------------------------------------------------TEST.c*/


#include "uCOS_II.h"


void fun(int x)


{


g_Var=x;


}


/* file4------------------------------------------------------MAIN.c*/


#include "uCOS_II.h"


void main(void)


{


g_Var=10;


}


/* --------------------------------------------------end of program*/


这种将全局变量定义在头文件中的方法,网上很多人调侃是钻空子,钻预编译命令的空子。赫赫,其实不管他是否钻空子,我认为这是一种不错的定义全局变量的方法,对于整个源程序变量的管理清晰直观。当然不是我这么认为,ucos作者这样做了,就有它的妙处。至于原理我大概介绍下:首先,我们可以看到全局变量g_Var在文件TEST.C和MAIN.C中有引用,它的定义是在头文件OS_uCOS_II.h 中,但是在定义的该变量用到了宏OS_EXT,但是该宏是否被“赋值”取决于是否定义OS_GLOBALS宏,也就是说OS_EXT是否被关键字extern替代是有条件的;然后,我们可以看到OS_GLOBALS宏在文件OS_uCOS_II.c中被定义,并且该文件包含了头文件OS_uCOS_II.h,根据include替代原则,头文件OS_uCOS_II.h所有信息将被包含进来,OS_GLOBALS被首先定义,那么OS_EXT就被定义为空,也即全局变量g_Var在该文件中被定义。同理可分析TEST.c文件和MAIN.c文件,由于这两个源文件只是包含头文件OS_uCOS_II.h,而没有#define OS_GLOBALS,因此OS_EXT将被关键字extern 替代,全局变量就只是作为声明出现。因此也就不会出现多次定义的连接错误。废话了这么多,其实就是条件编译语句的作用。


因此对于题目所提出的问题就有两种解释了。

微信扫一扫

第七城市微信公众平台