版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、第十二章 C语言中用户标识符的作用域和存储类在 C 语言中,标识符必须先定义后使用,但定义语句应该放在什么位置?在程序中,一个定义了的标识符是否随处可用?这些问题牵涉到标识符的作用域。经过赋值的变量是否在程序运行期间总能保存其值?这就牵涉到变量的生存期。当一个程序拓若干函数分别存放于不同的文件中时又有何限制?对于上述问题,C 语言中都有具体的规定。本章将对这些问题进行详细的讨论。本章中,读者应该特别注意“定义”和“说明”这两个词,“定义 definition)是指给变量分配确定的存储单元 说明”(declaration)只是说明变量的性质,而并不分配存储空间。12.1 局部变量、全局变量和存储
2、分类12.1.1 用户标识符的作用域在 C 语言中,由用户命名的标识符都有一个有效的作用域。所谓标识符的“作用域”就是指程序中的某一部分,在这一部分中,该标识符是有定义的,可以被 C 编译和连接程序所识别。例如我们知道每个变量有自己的作用域,在函数 sub 内定义的变量,不能在其它函数中引用;显然,变量的作用域与其定义语句在程序中出现的部位有直接的关系。据此可以划分出局部变量和全局变量。12.1.2 局部变量、全局变量和存储分类在函数内部或复合语句内部定义的变量,称为局部变量函数的形参也属于局部变量。在函数外部定义的变量,称为全局变量。有时,局部变量也被称为内部变量,全局变量被称为外部变量。C
3、 语言中,有两种存储类别:一种是自动类;一种是静态类。局部变量既可以说明成自动类;也可以说明成静态类;而全局变量只能是静态类。有四个与两种存储类别有关的说明符 ,它们是:autoregisterstatic(静态)和 extern与类型名一起出现,它们可以放在类型名的左边,也可以放在类型名的右边,例如:auto int i,j;也可写成:int auto i,j;存储类别确定了所有说明对象在内存中的存储位置,从而也确定了所说明对象的作用域和生存期。图 12.1 示意了一个C 程序在内存中的存储映象。动态存储区(堆栈)程序代码区图 12.1动态存储区用来保存函数调用时的返回地址、自动类别的局部变
4、量等等。静态存储区用以存放全局变量及静态类别的局部变量。12.2 局部变量及其作用域和生存期12.2.1 auto 变量当在函数内部或复合语句内定义变量时,如果没有指定存储类、或使用了 auto 说明符,系统就认为所定义的变量具有自动类别。因此:float a;就等价于:auto float a;auto 变量的存储单元被分配在内存的动态存储区。每当进入函数体(或复合语句)时,系统自动为 auto 变量分配存储单元;退出时自动释放这些存储单元另作它用。因此,这类局部变量的作用域是从定义的位置起,到函数体(或复合语句)结束为止。例如:void sub(float a)int i;|if(i0)i
5、nt n;|pintf(“%dn”,n)| a 和n 都是 auto i和 a 的作用域是整个sub函数;而 n 的作用域仅限于 if 子句内。注意:局部变量的定义必须放在所在函数体(或复合语句)中全部可执行语句之前。所有自动类局部变量的存储单元都是在进入这些局部变量所在的函数体(或复合语句)时生成,退出其所在的函数体(或复合语句)这就是自动类局部变量的“生存期当再次进入函数体(或复合语句)时,系统将为它们另行分配存储单元;因此变量的值不可能被保留。由于随着函数的频繁调用,动态存储区内为某个变量分配的存储单元位置随程序的运行而改变,变量中的初值也就随之而变,所以未赋初值的自动变量其值不定,自动
6、变量赋初值是在程序运行过程中进行的,每进入一次函数体使用这类局部变量的最突出优点是:可在各函数之间造成信息隔离,不同函数中使用了同名变量也不会相互影响。从而可避免因不慎赋值所导致的错误,影响到其它函数。12.2.2 register 变量寄存器变量也是自动类变量。它与 auto 变量的区别仅在于:用register 说明变量是建议编译程序将变量的值保留在 CPU的寄存器中,而不是象一般变量那样,占内存单元。程序运行时,访问存于寄存器内的值要比访问存于内存中的值快得多。因此当程序对运行速度有较高要求时,把那些频繁引用的少数变量,指定为register 变量,有助于提高程序的运行速度。例 12.1
7、 以下函数power 用以计算xn。#include main() int s;s=power(5,3);printf(%dn,s);power(int x,register int n) register int p;for(p=1;n;n-)p=p*x;return p;在 power 函数中,用作循环变量的n 和存放在连乘积的变量p 被定义为 register 变量,以便加快求值速度。说明:(1)CPU 中寄存器的数目是有限的,因此只能说明少量的寄存器变量。在一个函数中,允许说明为寄存器变量的数目不仅取决于 CPU的类型,也与所用的 C 编译程序有关。当没有足够的寄存器来存放指定的变量,
8、或编译程序认为指定的变量不适合放在寄存器中时,将自动按 auto register说明只是对编译程序的一种建议,而不是强制性的。(2)由于 register 变量的值是放在寄存器内而不是放在内存中,所以 register 变量没有地址,也就不能对它进行求地址运算。(3) register 变量的说明应尽量靠近其使用的地方,用完之后尽快释放,以便提高寄存器的利用效率。这可以通过把对 register 变量的说明和使用放在复合语句中来实现。12.2.3 静态存储类的局部变量当在函数体(或复合语句)内部,用 static 来说明一个变量时,可以称该变量为 静态局部变量。静态局部变量的作用域仍与 au
9、to、register 类的变量一样;但它与前者有两点本质上的区别:1.在整个程序运行期间,静态局部变量在内存的静态存储区中占据着永久性的存储单元。即使退出函数以后,下次再进入该函数时,静态变量仍使用原来的存储单元。由于并不释放这些存储单元,因此这些存储单元中的值得以保留;因而可以继续使用存储单元中原来的值。由此可知,静态局部变量的生存期将一直延长到程序运行结束。2.静态局部变量的初值是在编译时赋予的,在程序执行期间不再赋予初值。对未赋初值的静态局部变量,C 编译程序自动给它赋初值 0。静态局部变量的上述特点,对于编写那些在函数调用之间必须保留局部变量值的独立函数是非常有用的。12.3 全局变
10、量及其作用域和生存期全局变量只有静态一种类别 extern和static两种说明符。12.3.1 全局变量的作用域和生存期全局变量是在函数外部任意位置上定义的变量,它的作用域是从变量定义的位置开始,到整个源文件结束止。例如:void fun1(void);void fun2(void);int sum;main()/*定义全局变量*/ sum+; void fun1(void) sum+; int test;/*定义全局变量*/void fun2(void) sum+; test=1; 此处变量 sum 和 test 都是全局变量。sum 是在整个源程序的开始test 是在函数fun2 前定义
11、的,它的作用域从定义处开始直一以程序结束(只覆盖了fun2 全局变量的使用,相当于为函数之间的数据传递另外开辟了一条通道。上述三个函数中对变量 sum 的自增1 都是有效的。因此,使用中要特别小心,以免造成意料不到的改变。全局变量的生存期是整个程序的运行期间。若全局变量和某一函数中的局部变量同名,则在该函数中,此全局变量被屏蔽,在该函数内,访问的是局部变量,与同名的全局变量不发生任何关系。例 12.2#include int sum;/*定义全局变量*/void fun(void);main() sum=10;printf(*main(1)*:%dn,sum);fun( );printf(*m
12、ain(2)*:%dn,sum);void fun(void) int sum;sum=20;/*定义局部同名变量*/printf(* fun *:%dn,sum);以上程序运行时输出如下:*main(1)*:10* fun *:20*main(2)*:10即:全局变量sum 的作用域是除函数 fun以外的整个程序;局部变量 sum 的作用域仅是函数 fun内部。虽然全局变量作用域大,生存期长,用起来似乎方便灵活;但需要提醒读者的是:除十分必要外,一般不提倡使用全局变量,原因主要有以下三个方面:1.不论是否需要,全局变量在整个程序运行期间都占用内存空间。2.全局变量必须在函数以外定义,降低了函
13、数的通用性;影响了函数的独立性。3.使用全局变量,容易因疏忽或使用不当而导致全局变量中的值意外改变,从而引起副作用,产生难以查找的错误。12.3.2 在同一编译单位内用 extern 说明符来扩展全局变量的作用域当全局变量定义在后,引用它的函数在前时,应该在引用它的函数中用 extern 对此全局变量进行说明,以便通知编译程序:该变量是一个已在外部定义了的全局变量,已经分配了存储单元,不需再为它另外开辟存储单元。这时其作用域从 extern 说明处起,延伸到该函数末尾。注意:全局变量的说明与全局变量的定义不同:变量的定义(开辟存储单元)只能出现一次,在定义全局变量时,不可使用 extern 说
14、明符;而对全局变量的说明,则可以多次出现在需要的地方,这时必须用 extern 进行说明。12.3.3 在不同编译单位内用extern 说明符来扩展全局变量的作用域一个 C 程序总是由许多函数组成,这些函数可以分别存放在不同的源文件中,每个源文件可以单独进行编译,进行语法检查,若无错误即生成目标(.obj)文件,然后可用系统提供的连接程序把多个目标文件连接成一个可执行程序(.exe 当一个程序由多个编译单位组成,并且在每个文件中均需要引用同一个全局变量,这时若在每个文件中都定义了一个所需的同名全局变量,则在“连接”时将会产生“重复定义”的错误。在这种情况下,单独编译每个文件时并无异常,编译程序
15、将按定义分别为它们开辟存储空间;而当进行连接时,就会显示出错信息,指出同一个变量名进行了重复定义。解决的办法通常是:在其中一个文件中定义所有全局变量,而在其它用到这些全局变量的文件中用 extern 对这些变量进行说明,声明这些变量已在其它编译单位中定义,通知编译程序不必再为它们开辟存储单元。例 12.3/* file1.c */* file2.c */int x,y; /*定义全局变量*/ extern int x;/*说明全局变量*/main()fun2( )|printf(“%dn”,x); /*输出结果为 111*/fun1();fun2();|fun3();fun3()| x+;pr
16、intf(“%dn”,x); /*输出结果为112*/fun1( )|x=111;|以上例子中,在不同的编译单位内引用了全局变量 x。即:在文件 file2.c 中使用了文件 file1.c 中定义的全局变量。由于在 file2.c 中,说明语句 extern int x;放在了文件开始,所以变量 x 的作用域包含了file2.c 整个文件。若将这一说明改放在函数 fun2 内,变量 x 的作用域就只从说明的位置起延伸到函数 fun2 的末尾。也就是说,在函数fun3中将不能引用全局变量 x 了。12.3.4 静态全局变量当用 static 说明符说明全局变量时,此变量可称作“静态”全局变量。
17、静态全局变量只限于本编译单位使用,不能被其它单位所引用。12.4/* file1.c */static int n;void func( );main( )/* file2.c */extern int n;void func( ) printf(“file2:%dn”,n); n=5;|printf(“file1:%dn”,n);fun1( );文件 file1.c 定义了静态全局变量 n,在文件file2.c 中用 rxtern 说明 n 是全局变量,试图引用它。分别编译两个文件时一切正常;当把这两个文件连接在一起时将产生出错信息指出在文件 file2.c 中,符号n无定义。也就是说:在文
18、件 file1.c 中,变量n 虽然被定义成全局 static 说明后,其它文件中的函数就不能再引用它,而文件file2.c 在编译时由于用extern 说明了变量n n 开辟存储单元,所以在连接进就找不到 n static 说明限制了全局变量作用域的扩展,达到了信息隐蔽的目的。这对于编写一个具有众多编译单位的大型程序是十分有益的,程序员不必担心因全局变量重名而引起混乱。12.4 函数的存储分类所有函数在本质上都是外部的,因为 C 语言不允许在函数内部定义另一个函数。但当定义函数时,可以使用 extern 或static 说明符。12.4.1 用 extern 说明函数当定义一个函数时,若在函
19、数返回值的类型前加上说明符 extern时,称此函数为“外部”函数。extern 说明可以省略,一般的函数都隐含说明为 extern。所以,我们以前所定义的函数都属于外部函数。外部函数的特征是:可以被其它编译单位中的函数调用。通常,当函数调用语句与被调用函数不在同一编译单位,且函数的返回值为非整型时,应该在调用语句所在函数的说明部分用 extern 对所调用的函数进行函数说明。12.4.2 用static 说明函数当定义一个函数时,若在函数返回值的类型前加上说明符 static时,则称此函数为“静态”函数。静态函数的特征是:只限于本编译单位的其它函数调用它,而不允许其它编译单位中的函数对它进行
20、调用。从这个意义上说,静态函使用静态函数,可以避免不同编译单位因函数同名机时引起混乱。若强行调用静态函数,将会产生出错信息。习 题一、选择题12.1 以下程序输出的结果是)1,6,3,1,3 B)1,6,3,2,3C)1,6,3,6,3D)1,7,3,2,3main()int i=1,j=3;printf(“%d,”,i+);int i=0;i+=j*2;printf(“%d,%d,”,i,j);printf(“%d,%dn”,i,j);12.2 以下程序输出的结果是A)1 2 3 4main()B)4 3 2 1C)0 0 0 0D)4 4 4 4int a=1,2,3,4,i,x=0;fo
21、r(i=0;i4;i+) sub(a,&x); printf(“%d ”,x); printf(“n”);sub(int *s,int *y)static int t=3;*y=st; t-;12.3 以下程序输出的结果是A)8,17main()B)8,16C)8,20 D)8,8int k=4,m=1,p;p=func(k,m);printf(“%d,”,p);p=func(k,m);printf(“%dn”,p);func(int a ,int b)static int m,i=2;i+=m+1;m=i+a+b;return(m);12.4 以下程序输出的结果是A)34756B)23445 C)35745D)12345void fun(int *s)static int j=0;dosj+=sj+1;while(+j2);main()int k,a10=1,2,3,4,5;for(k=1;k3;k+) fun(a);for(k=0;k5;k+) printf(“%d”,ak);12.5 以下程序输出的结果是
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2026浙江安防职业技术学院高层次紧缺人才引进16人模拟试卷含答案详解【综合题】
- 2026恒丰银行福州分行社会招聘20人笔试模拟试题及答案详解
- 2026年南平市属学校紧缺急需学科教师招聘28人(二)笔试备考题库及答案详解
- 2026青海玉树州曲麻莱县暑期学生志愿服务专项行动招募笔试备考题库及答案详解
- 江西智萃花青生物科技有限公司招聘考试备考题库及答案详解
- 2026新疆兵团中心血站面向社会招聘事业单位工作人员1人笔试备考题库及答案详解
- 2026年甘肃定西临洮县峡口镇卫生院招聘乡村医生笔试参考题库及答案详解
- 2026年焦作市山阳区社区工作者招聘考试参考题库及答案详解
- 2025年鹤壁市山城区社区工作者招聘笔试试题及答案详解
- 2026年7月福建厦门市住房和建设局及所属部分事业单位招聘非在编辅助岗位人员11人笔试备考试题及答案详解
- 口腔护理入编试题及答案
- 2026《超龄劳动者基本权益保障暂行规定》解读
- 桥梁钢筋劳务分包合同
- 湖南农发环保科技有限责任公司招聘笔试题库2026
- 2026年交通辅警测试题及答案
- 2026天津华北地质勘查局及所属事业单位第二批招聘7人笔试备考试题及答案详解
- 全国矿产资源潜力评价总体实施方案
- 青岛大学2012年829物理化学考研真题
- 消防联动报警调试报告
- (完整版)牧场物语精灵驿站详细攻略
- 国家电网公司招聘高校毕业生应聘登记表
评论
0/150
提交评论