C语言全局变量的定义与声明
C语言中全局变量的定义与声明困扰着许多C语言初学者。本文讲述了全局变量定义与声明的用法,而且本为也将阐述这种用法的内在原理。我们先从两个错误例子引入,以下两个例程都在vc6.0平台上测试。
两种错误例程
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 。
C语言全局变量的定义与声明的更多相关文章
- 【转】c语言中的定义和声明
1. 变量的定义.声明 变量的声明有两种情况: 一种是需要建立存储空间的.例如:int a.在声明的时候就已经建立了存储空间.这种声明是"定义性声明(defining declaratio ...
- C++全局变量的定义和声明
编译单元 编译分为两个步骤: 第一步:将每个.cpp或.c和相应的.h文件编译乘obj文件(包含预编译,汇编.编译) 第二部:将obj文件进行Link,生成最终的可执行文件 根据该阶段错误大致可分为两 ...
- C语言中的定义与声明
什么是定义?什么是声明?它们有何区别? 举个例子: 1 2 A)int i; B)extern int i;(关于extern,后面解释) 哪个是定义?哪个是声明?或者都是定义或者都是声明?我所教过的 ...
- 详解keil采用C语言模块化编程时全局变量、结构体的定义、声明以及头文件包含的处理方法
一.关于全局变量的定义.声明.引用: (只要是在.h文件中定义的变量,然后在main.c中包含该.h文件,那么定义的变量就可以在main函数中作为全局变量使用) 方法1: 在某个c文件里定义全局变量后 ...
- keil采用C语言模块化编程时全局变量、结构体的定义、声明以及头文件包含的处理方法
以前写单片机程序时总是把所用函数和变量都写在一个c文件里,后来遇到大点的项目,程序动则几千行,这种方式无疑会带来N多麻烦,相信大家都有所体验吧! 后来学会了在keil里进行模块化编程,即只把功能相同或 ...
- c 语言中宏定义和定义全局变量的区别
宏定义和定义全局变量的区别: 1 作用时间不同. 宏定义在编译期间即会使用并替换,而全局变量要到运行时才可以. 2 本质类型不同. 宏定义的只是一段字符,在编译的时候被替换到引用的位置.在运行中是没有 ...
- C语言指针与数组的定义与声明易错分析
部分摘自<C语言深度解剖> 1.定义为数组,声明为指针 在文件1中定义: char a[100]; 在文件2中声明: extern char *a; //这样是错误的 这里的extern告 ...
- 小心C语言的定义与声明
小心C语言的定义与声明 转自360博客 注:为便于说明问题,文中提及的变量和函数都被简化. 一.起源 DBProxy在测试过程中,发现对其执行某步管理操作后,程序有时会崩溃,但不是每次都出现. 二.G ...
- C语言全局变量那些事儿
转自:http://blog.csdn.net/bingqingsuimeng/article/details/9405743 作为一名程序员,如果说沉迷一门编程语言算作一种乐趣的话,那么与此同时反过 ...
随机推荐
- 在JAVA中线程到底起到什么作用
这是javaeye上非常经典的关于线程的帖子,写的非常通俗易懂的,适合任何读计算机的同学. 线程同步 我们可以在计算机上运行各种计算机软件程序.每一个运行的程序可能包括多个独立运行的线程(Thread ...
- NSUInteger和NSInteger和int
int和NSInteger其实是差不多的,但更推荐使用NSInteger,因为使用NSInteger,这样就不用考虑设备是32位还是64位了. NSUInteger是无符号的,即没有负数,NSInte ...
- Storm集群扩容——从单机模式拓展到集群模式,以此类推
Storm是分布式的实时流处理系统,单机模式肯本不能体现其强大特点,尤其是当需要处理的数据很大很快的 时候,Storm可以随时扩容,而且操作非常简单,编写的应用程序自动负载均衡. 前面已经介绍了如何安 ...
- DELETE---删除记录
DELETE FROM table_name [WHERE conditions]; 例: DELETE FROM userinfo WHERE name='zhangsan'; 说明 1.不添加条件 ...
- ThinkPHP函数详解:import方法
import方法是ThinkPHP框架用于类库导入的封装实现,尤其对于项目类库.扩展类库和第三方类库的导入支持,import方法早期的版本可以和java的import方法一样导入目录和通配符导入,后来 ...
- js数组&&字符串&&定时器1
一.简单计算命令eval var str="6*5"; alert(eval(str)); 注意:不安全,一般都不会去用 二.id&&name id:只能唯一 ...
- ASP.NET MVC(三) TypeScript
TypeScript 是微软开发的 JavaScript 的超集,TypeScript兼容JavaScript,可以载入JavaScript代码然后运行.TypeScript与JavaScript相比 ...
- asp.net 开发问题:Web 服务器上的请求筛选被配置为拒绝该请求,因为内容长度超过配置的值。
"Web 服务器上的请求筛选被配置为拒绝该请求,因为内容长度超过配置的值." 这个问题在开发需要上传文件的时候可能会遇到,今天遇到这个问题,百度过也有挺多的修改方法. 方法1: 修 ...
- 通过Web.config中的configSections配置自己系统的全局常量
通过Web.config中的configSections配置自己系统的全局常量 随着系统的庞大,你的全局信息保存在appsitting里可能会比较乱,不如为模块写个自定义的全局常量吧 首先在Web.C ...
- Spring.net架构示例(含Aop和Ioc)源码
最近写了一个Spring.net的架构. 一.架构主图 架构图的数据流程走向是: UI层=>UILogic>=>Service>Business=>DataAccess ...