c语言中的局部变量和全局变量
每个变量都有属于它的作用域,就是这些变量在什么范围内有效; 我们按照变量的作用域范围可以把变量划分为局部变量和全局变量;
局部变量
局部变量出现在三种地方:
(1)在函数的开头定义的变量
(2) 在函数内的复合语句内定义的变量
(3)形式参数
在一个函数内部定义的变量只在本函数范围内有效,也就是只有本函数内才能引用它们,在此函数外不能使用这些变量;
在复合语句内定义的变量只能在本复合语句范围内有效,只有本复合语句内才能引用他们,在该复合语句外不能使用这些变量;
还有就是函数的形参,只在该函数内有效;
这三种情况下出现的变量就是局部变量,看下面的例子:
int fun(int x,int y)
{ int sum;
sum=x+y;
}
这个例子中,变量sum 定义在函数的开头,所以它是局部变量,它的作用域就是这个函数,出了这个函数,就会自动被销毁,无法被别处引用;同时 变量 x 和 y作为形参也是局部变量;
再看下面的例子:
int main()
{ int m,n,sum;
return 0;
}
在main 函数中定义的三个变量 m n max 都是局部变量,它们的作用域为这个 main 函数,并不因为在 main 函数中定义而在整个文件或项目中有效,同时 main 函数也不能使用其他函数中定义的变量;
再来看下面复合语句:
第8行定义的c 只在复合语句中有效,出了语句后就无效了,系统会把它占用的内存单元释放,所以第11行的打印会报错,显示变量 c 在 main中没有定义;
全局变量
一个源文件中可以包含若干个函数,在函数外部定义的变量就是全局变量或外部变量;全局变量为该源文件中所有函数所共有,它的作用范围是从变量定义的位置到源文件结束;看下面例子:
需要特别说明:
- 全局变量在程序的整个执行过程中都占据着内存单元,而不是仅仅在使用时才开辟存储单元;
- 设置全局变量的目的是为了增加函数间数据联系的渠道,由于同一个源文件中的所有函数都可以引用全局变量,所以在任意函数中改变全局变量的值,这就会影响到其他函数中全局变量的值,也就是说所有函数中的同一全局变量占用同一块地址;
- 为了区别局部变量和全局变量,通常将全局变量的第一个字母用大写表示;
来看下面全局变量的例子:
在第2行定义了全局变量a,然后又在第6行定义了局部变量a,全局变量的作用域是整个文件,而局部变量a的作用域是从第6行到第8行,在第7行的引用中,a的值会是局部变量的值,也就是说当局部变量与全局变量同名时,局部变量的值会将全局变量的值覆盖,所以打印结果如下:
建议在除了必要的情况下不要使用全局变量,理由是:
全局变量在整个执行过程中都占据中内存单元,它会使得函数的通用性降低,如果将一个函数移到另一个文件中时,还需要考虑把相关的全局变量及其值一起移过去,而且当该全局变量与其他文件的变量同名时就会出现问题,这会降低程序的可靠性和通用性;
变量的存储方式和生存周期
从上面可知,从变量的作用域角度来看,变量可分为全局变量和局部变量;还可以从另一个角度,即从变量值存在的事件来看,有的变量在程序整个运行期间都存在,而有的变量则是在调用其所在的函数时才会临时分配存储单元,在函数调用结束后该存储单元就被马上释放,变量就不存在了;
也就是说,变量的存储方式分为两种:静态存储方式和动态存储方式;
所谓静态存储是指在程序运行期间由系统分配固定的存储空间的方式,而动态存储则是在程序运行期间根据需要进行动态分配存储空间的方式;
先来看一下内存中的供用户使用的存储空间情况,可以划分为三个部分:
1)程序区
2)静态存储区
3)动态存储区
数据分别存放在静态和动态存储区;对于全局变量,它们都存放在静态存储区,在程序执行过程中占据固定的存储单元,而不会动态地进行分配和释放;而动态存储区中存放以下数据:
1)函数形式参数,在调用函数时给形参分配存储空间
2)函数中没由用关键字 static 声明的变量,即自动变量
3)函数调用时的现场保护和返回地址等
对以上数据,在函数调用开始时分配动态存储空间,函数结束时释放这些空间;
在C语言中,每一个变量和函数都有两个属性:数据类型和数据的存储类别 ;对于数据类型,我们比较熟悉,而对于存储类别指的是数据在内存中的存储方式,如动态存储和静态存储;
C语言中的存储类别包括4种:自动的(auto) 、静态的(static)、寄存器的(extern)、外部的(extern);根据变量的存储类别,可以知道变量的作用域和生存期;
局部变量的存储类别
1.自动变量(auto)
函数中的局部变量,如果不专门声明为 static 存储类别,都是动态地分配存储空间,数据存储在动态存储区中;
int f(int a) //定义函数f,a为形参
{
auto int b, c = 3;// b c 为自动变量
...
}
实际上,关键字 auto 可以省略,不写 auto 则默认为自动存储类别,它属于动态存储方式;
2.静态局部变量(static局部变量)
有时希望函数中的局部变量的值在函数调用结束后不消失而继续保持,即其占用的存储单元不变,在下一次调用该函数时该变量已有值(即上一次函数调用结束时的值),这时就该指定该局部变量为 静态局部变量,用关键字 static 进行声明
#include <stdio.h>
int main(void)
{
int f(int);//函数声明
int a = 2, i;//自动局部变量
for(i = 0; i < 3; i++)
{
printf("%dn", f(a));
}
}
int f(int a)
{
int b = 0;
static int c = 3;//静态局部变量
b = b + 1;
c = c + 1;
return(a + b + c);
}
运行结果如下:
注意:
1)静态局部变量属于静态存储类别,在静态存储区内分配内存单元,在整个程序运行期间都不释放;
2)对于静态局部变量时在编译时赋初值的,并且只赋初值一次,在程序运行时它已有初值,以后每次调用函数时不再重新赋初值而是保留上次函数调用后结束的值;对于自动变量,它赋初值是在函数调用时进行的,而不是在编译时进行,所以上面的 b= 0语句在每次调用函数时都会执行;
3)如果在定义局部变量时不赋初值的话,对于静态局部变量来说,编译时自动赋初值0(对于数值型变量)或空字符''(对于字符变量);而对于自动变量来说,它的值是一个不确定的值;
4)虽然静态局部变量在函数调用结束后任然存在,但是其他函数是不能引用它的,因为说到底它还是一个局部变量,作用范围仅限于本函数;
5)由于静态局部变量要长期占用内存,而且降低了程序的可读性,因此谨慎使用;
3.寄存器变量(register变量)
一般情况下,变量(包括静态存储方式和动态存储方式)的值都是存放在内存中的,当程序用到哪个变量的值时,由控制器发出指令将内存中该变量的值送到计算器中,然后再将计算结果送到内存中存放;但是如果有一些变量使用频繁(如10000次循环,每次循环都要引用某局部变量),则为了减少存取变量所花费的事件,可以将局部变量的值存在CPU的寄存器当中,需要时直接从寄存器中取出参加计算,此时这种变量就叫做 寄存器变量,用关键字 register 声明,如下:
register int f;
由于现在计算机的速度越来越快,优化的编译系统能够识别使用频繁的变量,从而将这些变量自动存放在寄存器中,因此实际上用 register 声明变量的必要性不大;
全局变量的存储类别
全局变量都是存放在静态存储区中的,因此它们的生存周期是固定的,存在于程序的整个运行过程;但是对于全局变量来说,还有一个问题需要确定,那就是它的作用域;
一般来说,外部变量是在函数外部定义的全局变量,它的作用域是从变量的定义处开始到本程序文件的末尾;在这个作用域内,全局变量可以为程序中各个函数引用,但有时程序设计人员希望能扩展外部变量的作用域,有以下几种情况:
1.在一个文件内扩展外部变量的作用域
如果外部变量不在文件的开头定义,其作用域就只限于定义出到文件借宿;如果出于某种考虑,在定义点之前的函数需要引用该外部变量,则可以在引用之前用关键字 extern 对该变量作 “外部变量声明”,表示把该外部变量的作用域扩展到此位置;如下:
#include <stdio.h>
int main(void)
{
int max(void);//函数声明
extern int A, B, C;//把外部变量A B C的作用域扩展到此处
printf("请输入三个整数: ");
scanf("%d %d %d", &A, &B, &C);
printf("max = %dn", max());
return 0;
}
int A, B, C; //定义外部变量A B C
int max(void)
{
int m;
m = A>B? A:B;//引用外部变量
if(C > m) m = C;
return(m);
}
结果如下:
2.将外部变量的作用域扩展到其他文件
如果一个C程序由多个源程序文件组成,那么在一个文件中想引用另一个文件中已经定义的外部变量,该如何做?举下面的例子:
如果一个C程序包括两个文件,在两个文件中都需要用到同一个外部变量NUM,此时不能分别在两个文件中各自定义一个外部变量NUM,否则在进行程序链接是会出现 重复定义 的错误;正确的做法是:在任一文件中定义外部变量NUM,而在另一文件中用 extern 对NUM 作 “外部变量声明”,即 extern NUM;这样在编译和链接时,系统就会知道NUM有 外部链接,可以从别处找到已定义的NUM,然后将NUM的作用域进行扩展;来看下面例子:
extern 既可以扩展外部变量在本文件中的作用域,也可以使得外部变量的作用域从一个文件扩展到其他文件,那么系统是怎么处理的?实际上,在编译时遇到 extern 时,先在本文件中寻找外部变量的定义,如果找不到就从其他文件中寻找外部变量的定义,如果找到了就将其作用域扩展到本文件;
3.将外部变量的作用域限制在本文件中
有时希望某些外部变量仅限在本文件中引用,而不能被其他文件引用,这时可以在定义外部变量时加一个 static 声明,如下:
加上 static 后,这种外部变量称为 静态外部变量;上面的例子,在file1 中定义了全局变量A,但是它使用了 static 声明,因此只能作用于本文件;