全局变量的定义与声明:
两种错误例程
1.unresolved external symbol
例子包含两个C文件(test.c)和(first.c)和一个头文件(test.h)。下边具体展示下它们的代码。
test.h内容
ifndef _TEST_H
#define _TEST_H
extern int count;
#endif
test.c内容
#include <stdio.h>
#include "test.h"
extern void Fis_Cal(void);
void main(void)
{
Fis_Cal();
printf("the present value of count is %d\n",count);
}
first.c内容
#include <stdio.h>
#include "test.h"
void Fis_Cal(void)
{
printf("the last value of count is %d\n",count);
count = 1;
}
错误分析:test.h头文件中声明了全局变量count,但是在两个C文件中都没有对count进行定义,所以才会出现unresolved external symbol。
一种解决方法:随便在两个C文件中加入一句“int count;”就OK了。例如我们加到test.c中,代码如下。
#include <stdio.h>
#include "test.h"
extern void Fis_Cal(void);
int count;
void main(void)
{
Fis_Cal();
printf("the present value of count is %d\n",count);
}
说明:加入的“int count;”就是对count的定义,默认的将其初始化为0。
结论:这种错误原因是“只声明未定义”。
2.multiply defined symbols found
还是如此,三个文件。但是,两个C文件与例程一中的文件一样,改动的只是头文件。
test.h内容
#ifndef _TEST_H
#define _TEST_H
int count;
#endif
可以看到,与例程一仅仅差了一个“extern”关键词。
错误分析:test.h头文件中定义了全局变量count,但是在两个C文件都通过“#include "test.h"”这句话对“int count;”进行了引用,所以造成了重复定义的错误。
一种解决方法:添加一个“first.h”的头文件,并且更改first.c的内容,具体更改如下。
first.h内容
#ifndef _FIRST_H
#define _FIRST_H
extern int count;
#endif
first.c内容
#include <stdio.h>
#include "first.h"
void Fis_Cal(void)
{
printf("the last value of count is %d\n",count);
count = 1;
}
说明:经过这样的修改,原来的test.c中就包含了count的定义,而first.c中就包含了对count的声明,重复定义错误就得到解决。
结论:这种错误原因是“多个C程序都包含了定义全局变量的头文件”。
原理分析
我认为“int count;”是对全局变量的定义,而“extern int count”是对全局变量的声明,目的是让其他文件也使用这个全局变量。下边我们来挖掘全局变量的定义与声明的内涵。
全局变量要么初始化(非零),要么没有初始化(为零)。非零时存储在程序中的data段,零时存储在程序的bss段。这谈了程序(.bin或者.hex)的结构。我再讲一下程序的启动,程序在启动(boot)过程中,通常都会运行一个叫bootloader的引导程序,这个引导程序干了很多事情,其中有一最重要的任务就是把程序(test段和rodata段)拷贝到内存,还包括data段的拷贝和bss段初始化。我们着重讲一下data段的拷贝和bss段初始化。
我们的编译器会为我们定义的全局变量分配内存(地址),而且给我们的全局变量赋初值(写内存或清零),以后我们的程序就会根据需要来读这个全局变量(地址)或者修改这个全局变量(写内存)。初值为零时就在bss段,这个段初始化代码会将这部分清零。初值非零时,初始化代码会将全局变量的初值拷贝到data段。
那么,显然全局变量的初值只有一个。我们程序中的全局变量的定义就是对全局变量分配内存并赋初值。而全局变量的声明是为了跨文件使用全局变量的需要,通过"extern"关键词来将全局变量引出。
顺便说一下C语言的存储类说明符,这能帮助我们加深理解。
C语言的存储类说明符
Auto 只在块内变量声明中被允许, 表示变量具有本地生存期。
Extern 出现在顶层或块的外部变量函数与变量声明中,表示声明的对象具有静态生存期, 连接程序知道其名字。
Static 可以放在函数与变量声明中,在函数定义时,只用于指定函数名,而不将函数导出到链接程序,在函数声明中,表示其后边会有定义声明的函数,存储类型static.在数据声明中,总是表示定义的声明不导出到连接程序关键字。
一种更好的声明与定义方式
test.h内容
#ifndef _TEST_H
#define _TEST_H
#ifdef GLOBALS
int count;
#else
extern int count;
#endif
#endif
test.c内容
#define GLOBALS
#include <stdio.h>
#include "test.h"
extern void Fis_Cal(void);
void main(void)
{
Fis_Cal();
printf("the present value of count is %d\n",count);
}
first.c内容
#include <stdio.h>
#include "test.h"
void Fis_Cal(void)
{
printf("the last value of count is %d\n",count);
count = 1;
}
说明:这种方法可以只定义一个头文件实现在不同C文件中分别实现定义与声明。“#define GLOBALS”只在当前定义的test.c文件中有效,所以在test.c中#include "test.h"预处理后,加入的是int count,而first.c中加入的"extern int count;"。其实还有一种书写方法,也能实现这个效果。
test.h内容
#ifndef _TEST_H
#define _TEST_H
#ifdef GLOBALS
#define EXT
#else
#define EXT extern
#endif
EXT int count;
#endif
typedef struct:
typedef struct OLNode
{
int i,j;
int data;
OLNode *right,*down;
}OLNode,*OLink;//结构的对象OLNode, 指向结构的指针*OLink,
可以这样写 struct OLNode{}; OLNode OLNode, *OLink;
分三块来讲述:
1 首先://注意在C和C++里不同
在C中定义一个结构体类型要用typedef:
typedef struct Student
{
int a;
}Stu;
于是在声明变量的时候就可:Stu stu1;(如果没有typedef就必须用struct Student stu1;来声明)
这里的Stu实际上就是struct Student的别名。Stu==struct Student
另外这里也可以不写Student(于是也不能struct Student stu1;了,必须是Stu stu1;)
typedef struct
{
int a;
}Stu;
但在c++里很简单,直接
struct Student
{
int a;
};
于是就定义了结构体类型Student,声明变量时直接Student stu2;
======================================================================================
2.其次:
在c++中如果用typedef的话,又会造成区别:
struct Student
{
int a;
}stu1;//stu1是一个变量
typedef struct Student2
{
int a;
}stu2;//stu2是一个结构体类型=struct Student
使用时可以直接访问stu1.a
但是stu2则必须先 stu2 s2;
然后 s2.a=10;
======================================================================================
3 掌握上面两条就可以了,不过最后我们探讨个没多大关系的问题
如果在c程序中我们写:
typedef struct
{
int num;
int age;
}aaa,bbb,ccc;
这算什么呢?
我个人观察编译器(VC6)的理解,这相当于
typedef struct
{
int num;
int age;
}aaa;
typedef aaa bbb;
typedef aaa ccc;
也就是说aaa,bbb,ccc三者都是结构体类型。声明变量时用任何一个都可以,在c++中也是如此。但是你要注意的是这个在c++中如果写掉了typedef关键字,那么aaa,bbb,ccc将是截然不同的三个对象。
//此处不是很理解。
typedef struct和struct的区别:
typedef struct tagMyStruct
{
int iNum;
long lLength;
} MyStruct;
上面的tagMyStruct是标识符,MyStruct是变量类型(相当于(int,char等))。
这语句实际上完成两个操作:
1) 定义一个新的结构类型
struct tagMyStruct
{
int iNum;
long lLength;
};
分析:tagMyStruct称为“tag”,即“标签”,实际上是一个临时名字,不论是否有typedefstruct 关键字和tagMyStruct一起,构成了这个结构类型,这个结构都存在。
我们可以用struct tagMyStruct varName来定义变量,但要注意,使用tagMyStruct varName来定义变量是不对的,因为struct 和tagMyStruct合在一起才能表示一个结构类型。
2) typedef为这个新的结构起了一个名字,叫MyStruct。
typedef struct tagMyStruct MyStruct;
因此,MyStruct实际上相当于struct tagMyStruct,我们可以使用MyStruct varName来定义变量。
2.
typedef struct tagMyStruct
{
int iNum;
long lLength;
} MyStruct;
在C中,这个申明后申请结构变量的方法有两种:
(1)struct tagMyStruct 变量名
(2)MyStruct 变量名
在c++中可以有
(1)struct tagMyStruct 变量名
(2)MyStruct 变量名
(3)tagMyStruct 变量名