8.8 局部变量和全局变量
8.8.1 局部变量
局部变量:函数或分程序内定义的变量,只在本函数或本分程序内有效。
说明:(1)我们使用的变量一般是局部变量,main函数中的变量也是局部变量。
(2)不同函数可以使用相同的变量名,但他们是互不关联的两个变量
(3)形参是局部变量
(4)复合语句中的变量局部于本复合语句(分程序)
8.8.2 全局变量
全局变量:在函数(所有函数)之外定义的变量,作用于其后的所有函数,叫做全局变量。
作用域(有效范围):定义变量的位置到本文件结束。
局部变量与全局变量尤如地方法规与全国法规。
说明
(1)优点:增加了函数的联系渠道,带回多于一个返回值。
部分的节省了内存和执行时间。
(2)缺点:始终占用内存。
降低了函数的独立性。
降低了函数的可靠性和通用性。
模块化程序设计有一个原则:模块的“内聚性”强,与其他模块的“耦合性”小。
内聚性即模块内部的内在完整性,独立性,功能的单一性,封闭性。
耦合性:模块之间的互相影响,函数与数据的相关性。
耦合性小,不确定因素就少。
习惯上我们把函数做成“黑箱”,只关心入口(形参和实参结合)和出口参数(函数值)及其函数功能,不关心模块(函数)内部的细节。
这样,程序的可移植性好,可读性好。
全局变量的值不好控制,过多的使用全局变量会极大的降低程序的可读性
因此我们应尽量不使用全局变量
全局变量举例:使用软件的单位名,年,月,日
提倡:全局变量的第一个字母为大写。
(3)外部变量的屏蔽
li8_16.c 程序分析的方法
8.9 变量的存储类别
8.9.1 动态存储方式与静态存储方式
作用域:空间
生存期:时间
存储区分类:程序区、静态存储区、动态存储区
全局变量全部存放在静态存储区,程序开始执行时分配存储区,程序运行期间占据固定的存储空间,程序执行完毕后释放掉。
动态存储方式:在程序运行期间根据需要进行动态的分配存储空间,函数调用开始时动态分配存储区,函数结束时释放这些空间。
形参、自动变量(无static 声明的局部变量)存放在动态存储区、函数调用时的现场保护和返回地址等也存放在动态存储区(堆栈)。
同一函数两次调用时局部变量占据的存储空间地址可能不同。
被调用函数局部变量的生存期并不等于整个程序的生存期。
变量和函数有两个属性:数据类型、数据的存储类别。
存储类别:数据在内存中存储的方法:静态存储类、动态存储类。
根据存储类别决定变量的作用域和生存期。
8.9.2 auto变量
auto变量:无static声明的局部变量。
用auto作存储类别的声明时,可以不写auto,存储类别隐含确定为auto(自动存储类别)。
是动态存储方式。
大多数变量是自动变量。
8.9.3 用static声明局部变量
用static声明的局部变量是静态局部变量。
函数调用结束后静态局部变量占据的内存存储单元空间不释放,局部变量保留原值,下次调用时可以继续使用该值。
li8_17.c
说明:
(1)静态局部变量属于静态存储类别,在静态存储区分配内存单元,在整个程序运行期间都不释放。
使用静态局部变量的值
(2)静态局部变量的初值:编译时赋初值,只赋初值一次,调用时已经有初值,调用时是不赋初值的。
(3)若定义时未赋初值,则初始化为0或'\0'(全0)
(4)静态局部变量中“局部”的含义:只有本函数可以使用,其他函数无法使用该变量。
用途:
(1)需要保留函数上一次调用的某些结果时,这些值可定义为静态变量。
li8_18.c 阶乘
(2)某些变量初始化后只被引用而不改变其值时,可定义为静态变量。
(3)静态变量多占内存,降低可读性,没有必要时不要使用静态变量。
常见用法:统计某一个函数被调用的次数,或者与调用次数相关的其他统计工作。
函数调试的方法:
1 监控函数的参数值
2 加断点
8.9.4 register 变量
省去了内存与CPU的数据交换过程,直接使用CPU的内部寄存器。
li8_19.c
说明:(1)只有局部变量和形参可以作为register变量,全局变量不行。
(2)80x86系列CPU最多可使用的register变量数目有限。
int型可使用8个通用寄存器。
实际上有些系统并不把register变量存放在寄存器中,而优化的编译系统则可以自动识别使用频繁的变量而把他们放在寄存器中,因此用register声明变量实际上是不必要的。
我们只需要知道有这么一种变量即可。
(3)静态变量不能定义为register。
8.9.5 用extern声明外部变量
外部变量即全局变量
可以用extern声明来改变全局变量的作用域
1. 在一个文件内声明外部变量
一般将外部变量声明放在引用它的函数之前,也可以将外部变量定义放在引用它的函数之前。
li8_20.c
外部变量声明时可以不指定类型。
2. 在多文件的程序中声明外部变量
在一个文件中引用另一个文件中定义的外部变量时,应用extern声明该变量,且用#include将该文件包含进来。
li8_21.c
8.9.6 用static声明外部变量
静态外部变量:只能用于本文件的外部变量(全局变量)
static改变了外部变量的作用范围
8.9.7 关于变量的声明和定义
函数的声明与定义:定义是一个模块,而声明放在声明部分是一个说明;
对于变量:定义性声明需要建立存储空间和初始化,即定义,定义只能有一次。
引用性声明:已经在别处定义过了,做一个提前使用的说明,可以多次说明。
对于外部变量,extern只用于声明,不用于定义。
用static声明局部变量,是做静态分配。
用static声明全局变量,是限定全局变量的作用域。
auto、register、static只能在定义时使用。
8.9.8 存储类别小结
对一个数据的定义包括两个属性,数据类型和存储类别。
1 作用域:
局部变量:自动变量、静态变量、(寄存器变量)、形式参数。
全局变量:静态外部变量、外部变量
2 生存期:
动态存储:自动变量、(寄存器变量)、形式参数
静态存储:静态变量、全局变量、静态外部变量
3 存储位置
静态存储区:静态变量、静态外部变量、全局变量
动态存储区:自动变量、形式参数
4 作用域和生存期的比较page181
作用域又称为变量的“可见性”
生存期表示变量是否存在
静态变量的作用域和生存期是不同的。
5 static的含义对于局部变量和全局变量是不同的,相同的是限制了作用域。
不同的是局部变量原来是动态存储,而全局变量本来就是静态存储。
变量的深入探讨与内存的使用是密切相关的
8.10 内部函数和外部函数
函数本质上是全局的,但可以限定函数能否被别的文件所引用。
8.10.1 内部函数
只能被本文件的其他函数所调用的函数称为内部函数。
在定义时用static加以说明
又称为静态函数
8.10.2 外部函数
可以被其他文件调用的函数称为外部函数,一个函数如果不加说明时是外部函数
外部函数可用extern加以说明,也可以不说明。
li8_22.c
在调用别的文件的外部函数时,应用extern声明,但是可以省略extern关键字。
即函数原型可以拓宽函数的作用域。
#include 预处理命令使用户可以免写库函数原型。
8.11 如何运行一个多文件的程序
1 TC的项目文件 .prj
将多个.c文件的文件名排列起来,保存为.prj,编译连接即可
2 MS-C的link命令
3 用#include,后边的文件被插入到第一个文件之前,extern声明可以省略。
习题
xt8.1 xt8.3 xt8.4 xt8.9 xt8.11 xt8.14 xt8.15 xt8.17 xt8.18
算法总结
例8.6 例8.8 例8.9 例8.14 例8.16 例8.17 例8.22
小结
1 函数概念:具有一定功能一定形式的程序单位。
2 一般将输入和输出放在main()函数中,程序主体另外定义为函数
3 函数定义与函数原型
4 值传递和址传递
5 参数传递和“哑实结合”,一一对应
6 函数返回值及类型
7 递归调用的特点
9 数组做实参与址传递
10 全局变量
11 静态变量与初始化
运行到a=5时:
运行到。