《C语言与程序设计教程》课件第9章.ppt
- 【下载声明】
1. 本站全部试题类文档,若标题没写含答案,则无答案;标题注明含答案的文档,主观题也可能无答案。请谨慎下单,一旦售出,不予退换。
2. 本站全部PPT文档均不含视频和音频,PPT中出现的音频或视频标识(或文字)仅表示流程,实际无音频或视频文件。请谨慎下单,一旦售出,不予退换。
3. 本页资料《《C语言与程序设计教程》课件第9章.ppt》由用户(momomo)主动上传,其收益全归该用户。163文库仅提供信息存储空间,仅对该用户上传内容的表现方式做保护处理,对上传内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知163文库(点击联系客服),我们立即给予删除!
4. 请根据预览情况,自愿下载本文。本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
5. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007及以上版本和PDF阅读器,压缩文件请下载最新的WinRAR软件解压。
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- C语言与程序设计教程 语言 程序设计 教程 课件
- 资源描述:
-
1、第第9章章 C语言与程序设计补遗语言与程序设计补遗 9.1 变量的存储类别与生命期9.2 指向函数的指针变量9.3 带参数的主函数main9.4 编译预处理命令9.5 枚举类型9.6 位运算 9.1 变量的存储类别与生命期变量的存储类别与生命期 1.生命期的概念生命期的概念从变量生命期(即由创建到撤消)来分,可以将变量分为静态存储变量和动态存储变量两类:(1)静态存储变量:在程序运行时固定分配存储空间的变量。(2)动态存储变量:在程序运行中根据需要动态分配存储空间的变量。程序运行时对应的内存分配示意如图9-1所示。图9-1 程序运行时对应的内存分配示意全局变量和静态局部变量(static变量)
2、存放在静态数据区,程序开始执行时给它们分配内存单元,程序执行结束时再释放这些内存单元。也即在程序的整个执行过程中这些变量都存在(有自己的内存单元),它们的生命期为程序的整个执行过程。动态数据区存放自动局部变量、形参变量和用于中断现场的保护数据。自动局部变量是指未加staic声明的局部变量;形参变量是指函数的形参。在函数调用时为自动局部变量和形参变量在动态数据区分配内存单元,当函数执行结束时释放这些内存单元。也即在函数的整个执行过程中这些变量都存在,它们的生命期为函数的整个执行过程。在C语言中,每个变量都有两个属性:数据类型和数据的存储类别。前面各章节中,我们在定义变量时只涉及它的数据类型,其实
3、还可以定义变量的存储类别,它决定这个变量的存放位置(是静态数据区还是动态数据区)和生命期。变量定义的一般形式如下:存储类别 类型标识符 变量名;其中,方括号“”中的内容为可选项。C语言中的变量可以有4种存储类别:自动变量、寄存器变量、静态变量和外部变量,分别用存储类别auto、register、static和extern。下面仅对自动变量、寄存器变量和静态变量进行介绍。2.自动变量在函数体内或复合语句内定义变量时,如果没有指定存储类别或使用了“auto”存储类别,则系统都认为所定义的变量为自动局部变量,简称为自动变量。此外,函数首部中的形参也是自动变量。例如:auto int a=2,b;in
4、t a=2,b;上述两种定义方法是等价的,即都定义了a和b为自动变量。每当进入函数体或复合语句时,系统在动态数据区为自动变量分配临时内存单元,退出时自动释放这些内存单元;再次进入函数或复合语句时,系统又为它们重新分配临时内存单元,退出时又自动释放这些内存单元。因此,释放后自动变量的值不可能保留,这类变量的作用域及生命期只存在于定义它的函数体内或复合语句内。自动变量在动态数据区分配内存单元,并随着程序的运行可能不断释放和重新分配内存单元,也即这个内存单元的位置是不固定的,因此自动变量中的值也会随之改变。所以,自动变量在使用之前必须赋值,否则它的值是不确定的。此外,在不同函数中使用的同名自动变量也
5、不会相互影响。例9.1 分析下面程序的运行结果。#includevoid fun();void main()fun();fun();void fun()int n=2;/*自动变量*/n+;printf(n=%dn,n);解 在程序中,函数fun中定义的n为自动变量,其作用域只在函数fun内。第一次调用fun时,为n分配临时内存单元且n的初值为2,执行“n+;”后n值为3,因此输出结果为3;第一次调用fun结束,此时分配给n的内存单元被释放。第二次调用fun时,又为n重新分配了内存单元,函数fun的执行过程与第一次一样,因此输出的结果仍是3。程序执行的动态图如图9-2所示。图9-2 程序执行的
6、动态图程序执行后的输出结果为:n=3n=33.寄存器变量寄存器变量也是自动变量,它与一般自动变量的区别在于,寄存器变量的值是存储于CPU内的寄存器中,而一般的自动变量则存储于内存中。由于从寄存器中读取数据要比从内存中读取数据的速度快,所以为了提高运算速度,可以将一些频繁使用的局部变量或形参变量定义为寄存器变量。寄存器变量只要在定义时加上存储类别register即可。例如:register int a;使用寄存器变量时要注意以下几点:(1)寄存器变量本身是一个自动变量,因此只有函数内定义的变量或形参才可以定义为寄存器变量。(2)CPU中的寄存器个数有限,所以只能将少数的变量定义为寄存器变量。(3
7、)受寄存器长度的限制,寄存器变量只能是char、int和指针类型的变量。(4)由于寄存器变量是保存在CPU的寄存器中而不是保存在内存中,因此不能进行取地址运算。(5)在调用函数时,函数中的寄存器变量才占用寄存器存放其值,当函数调用结束时就释放寄存器,也即寄存器变量消失。例9.2 编写求n!的程序。解 程序如下:#includelong fac(int n);void main()int n;long f;printf(Input n=);scanf(%d,&n);f=fac(n);printf(%d!=%ldn,n,f);long fac(int n)register long t=1;reg
8、ister int k;for(k=2;k=n;k+)t=t*k;return(t);运行结果:Input n=55!=120在程序中,由于函数fac中的变量t和k频繁使用,故将其定义为寄存器变量。4.静态变量静态变量的存储空间为内存中的静态数据区,该区域中的数据在整个程序运行期间一直占用分配给它们的存储空间,直到整个程序结束。特别要注意的是,函数体内如果在定义静态变量(称为局部静态变量)的同时进行了初始化,则以后程序不再对其进行初始化操作。这是由于第一次遇见局部静态变量时,系统即为局部静态变量分配了专用的内存单元并将初始化值送入这个内存单元,此后该局部静态变量就一直使用这个内存单元,而无论函
9、数的调用或结束;也即,下一次函数调用时,这个局部静态变量仍然使用这个内存单元,而且并不重新初始化。这样,局部静态变量就可以保存前一次函数调用得到的值而用于下一次函数调用;这一点是局部静态变量与自动变量的本质区别。局部静态变量的初值是在程序编译时赋予的,在程序执行过程中不再赋值。对没有赋初值的局部静态变量,编译系统自动给它赋初值0。auto型局部变量与static型局部变量的区别如表9.1所示。表表9.1 auto型局部变量与型局部变量与static型局部变量的区别型局部变量的区别例9.3 分析下面程序的运行结果。#includevoid fun();void main()fun();fun()
10、;void fun()static int n=2;/*局部静态变量*/n+;printf(n=%dn,n);解 在程序中,函数fun中定义的n为局部静态变量;其作用域只在函数fun内。在程序执行开始前,系统已为n分配了内存单元且n的初值为2,第一次函数fun调用时,执行“n+;”后n值为3,输出3;第一次调用fun函数结束,但系统分配给n的内存单元并不释放。第二次调用fun时,执行“n+;”后n值由3变为4(注意,不执行对静态局部变量的初始化操作static int n=2),故输出结果为4。所以,程序执行后的输出结果为:n=3n=4 例9.4 分析下面程序的运行结果。#includeint
11、 fun(int x,int y);void main()int j=4,m=1,k;k=fun(j,m);printf(%d,k);k=fun(j,m);printf(%dn,k);int fun(int x,int y)static int m=0,i=2;i=i+m+1;m=i+x+y;return m;解 程序执行的动态图如图9-3所示。图9-3 程序执行的动态图由于静态局部变量在每次函数调用结束时并不消失,所以在动态图中,我们将静态局部变量m和i放置于函数fun空间的开始处,并且当函数fun结束时,它们仍然存在(在动态图上是用一条横线将它们与局部自动变量分开,且这条线一直持续到程序结
12、束)。在下一次调用函数fun时,仍可使用它们的值。由动态图可知,程序的运行结果为:8,17。9.2 指向函数的指针变量指向函数的指针变量在C语言中,一个函数所对应的程序代码总是存放在一段连续的内存区域内,并且像数组名代表数组的首地址一样,函数名就代表该函数代码所占内存区域的首地址。不同的函数有不同的首地址。因此,函数名可以看做为是一个广义的变量,我们可以把这个“变量”函数名(函数的首地址)赋给一个指针变量,使该指针变量指向这个函数,然后通过指针变量就可以找到并且调用执行这个函数。这种指向函数的指针变量就称为“函数指针变量”。1.函数指针变量的定义与初始化函数指针变量定义的一般形式为类型说明符(
13、*指针变量名)();其中,类型说明符表示被指向的那个函数的返回值类型,“(*指针变量名)”表示“*”后面的变量名是一个指针变量,最后的空括号“()”表示这个指针变量的指向是一个函数。例如:int(*p)();定义了p是一个指向函数的指针变量,该函数的返回值为整型。p是用来存放函数的入口地址的,在没有赋值前它不指向任何一个具体函数,并具有一个空指针值。想要通过函数指针变量来实现对某个函数的调用,还必须对函数指针变量进行初始化,即将需要调用的某个函数入口地址赋给它;由于函数名代表该函数的入口地址,因此是将某个函数名赋给函数指针变量。函数指针变量初始化的方式有两种:一种是直接赋值;一种是加地址运算符
14、“&”赋值。格式如下:函数指针变量名=函数名;或者函数指针变量名=&函数名;也可以在函数指针变量定义时进行初始化:类型说明符(*指针变量名)(形式参数表)=函数名;或者类型说明符(*指针变量名)(形式参数表)=&函数名;函数的指针变量与普通指针变量都能实现间接访问,其唯一的区别是:普通指针变量指向的是内存的数据存储区,而函数的指针变量指向的是内存的程序代码区。因此,普通指针变量的“*”运算是访问内存中的数据,而函数的指针变量执行“*”运算时,其结果是使程序控制转移到由函数指针变量所指向的函数入口地址,并开始执行该函数。此外,形式参数表也与第5章函数中的形式参数表不同,只能给出形参的类型。2.用
15、函数指针变量调用函数定义了函数指针变量并初始化后,就可以在程序中通过函数指针变量来调用所需要的函数了。调用函数的一般形式为(*指针变量名)(实参表)由于优先级不同,所以“*指针变量名”必须用圆括号“()”括起来,表示间接调用指针变量所指向的函数,而后面的圆括号“()”中的内容为传递给被调函数的实参。例9.5 用函数指针调用函数的方式实现在两数中找出最大数的程序。#includeint max(int a,int b);void main()int(*p)(int,int);int x,y,z;p=max;printf(Input two numbers:n);scanf(%d,%d,&x,&y
16、);z=(*p)(x,y);printf(max=%dn,z);int max(int a,int b)if(ab)return(a);elsereturn(b);运行结果:Input two numbers:88,66max=88从程序中可以看出,用函数指针变量形式调用函数的步骤如下:(1)先定义函数指针变量;如程序中的“int(*p)(int,int);语句”。(2)将被调函数的入口地址(即函数名)赋给函数指针变量;如程序中的“p=max;”语句。(3)用函数指针变量形式来调用函数;如程序中的“z=(*p)(x,y);”语句。例9.6 分析下面程序运行的结果。#includefloat f
17、1(float n);float f2(float n);void main()float(*p1)(float),(*p2)(float),(*t)(float),y1,y2;p1=f1;p2=f2;y1=p2(p1(2.0);t=p1;p1=p2;p2=t;y2=p2(p1(2.0);printf(%3.0f,%3.0fn,y1,y2);float f1(float n)return n*n;float f2(float n)return 2*n;解 程序中,函数f1实现的是返回参数值的平方值,函数f2实现的是返回参数值的2倍。在主函数main中定义了三个函数指针变量p1、p2和t,语句“
18、p1=f1;p2=f2;”让函数指针变量p1指向函数f1,函数指针变量p2指向函数f2,然后调用“p2(p1(2.0)”,即先让2.0平方后再乘以2,即结果为8并赋给变量y1。接下来,语句“t=p1;p1=p2;p2=t;”交换了p1和p2的指向,即此时p1指向f2,p2指向f1。再次调用“p2(p1(2.0)”,则是先让2.0乘以2然后再平方,即结果为16并赋给y2。因此,最后输出的y1和y2值为:8,16。使用函数指针变量还应注意以下几点:(1)函数指针变量不能进行算术运算,这与数组指针变量不同;数组指针变量加减一个整数可以使指针移向后面或前面的数组元素,而函数指针的移动则毫无意义。(2)
19、函数指针变量定义时“(*指针变量名)”的圆括号“()”不能缺省,有了括号指针变量名先和“*”结合,表示定义的变量名是一个指针变量。如果缺省了圆括号“()”,即如下面所示:int*p(int,int);则表示定义的p是一个函数;p为函数名,其前面的“*”表示函数p是返回指针值的函数,也即意思和功能完全不同了。(3)要注意函数指针变量与指向二维数组的指针变量之间的区别。如:“int(*p)(int);”和“int(*p)4;”,前者是函数指针变量,后者是指向二维数组的指针变量。3.函数的指针变量作为函数的参数函数的形参可以是各种类型的变量,也可以是指向函数的指针变量。形参是指向函数的指针变量时可以
20、接受实参传来的不同函数,这种参数传递不是传递任何数据或普通变量的地址,而是传递函数的入口地址。当函数参数在两个函数之间传递时,调用函数的实参应该是被调函数的函数名,而被调函数的形参应该是接受函数地址的函数指针变量。例9.7 任意输入两个整数,求它们的和、差、积、商。解 程序设计如下:#includeint add(int x,int y);int sub(int x,int y);int mul(int x,int y);int div(int x,int y);int fun(int(*p)(int,int),int x,int y);void main()int a,b;printf(In
21、put a,b=);scanf(%d,%d,&a,&b);printf(%d+%d=%dn,a,b,fun(add,a,b);printf(%d-%d=%dn,a,b,fun(sub,a,b);printf(%d*%d=%dn,a,b,fun(mul,a,b);printf(%d/%d=%dn,a,b,fun(div,a,b);int add(int x,int y)return x+y;int sub(int x,int y)return x-y;int mul(int x,int y)return x*y;int div(int x,int y)return x/y;int fun(int
22、(*p)(int,int),int x,int y)int z;z=(*p)(x,y);return z;运行结果:Input a,b=12,412+4=1612-4=812*4=4812/4=3程序中的add、sub、mul和div是已经定义过的函数,函数fun中的形参p是函数指针变量,主函数main调用fun函数:fun(add,a,b)则将函数add的入口地址传给了函数指针变量p,而a、b值分别传给了形参x、y;即函数fun中的语句“z=(*p)(x,y);”此时相当于语句“z=add(x,y);”,从而实现了对a与b的求和;其他函数调用也是如此。9.3 带参数的主函数带参数的主函数ma
23、in 前面各章介绍的主函数main都是不带参数的,因此main后的括号是空括号“()”。实际上主函数main是可以带参数的,这个参数可以认为是主函数main的形参。C语言规定主函数main的参数只能有两个:argc和argv,并且第一个形参argc必须是整型变量,第二个形参argv必须是指向字符串的指针数组。也即,主函数main的一般形式为:void main(int argc,char*argv)函数体其中,argc称做参数计数器,它的值是包括命令名在内的参数个数,因此其值至少为1;argv指针数组的作用是存放命令行中命令名及每个参数字符串的首地址。由于主函数main是最先执行的,因此不可能
24、在程序内部获得实参值。所以,主函数main的实参值是从操作系统的命令行上获得的。当需要运行一个可执行文件时,在DOS提示符下键入文件名及实参值,即可把这些实参传给main的形参。带参数的主函数main的调用形式如下:可执行文件名 参数1 参数2 参数n上面这一行字符称为命令行,是在DOS系统提示符下键入的。其中,可执行文件名称为命令名,其后的参数称为命令行参数,命令名与各参数之间用空格分隔。例如:c:file1 China Beijing由于命令名file1本身也算是一个参数,所以共有三个参数,因此argc的值取3,argv指针数组中元素的值为命令行中各字符串(参数均按字符串处理)的首地址。指
25、针数组的大小即为参数个数;数组元素的初值由系统自动赋予。上述命令行参数在赋给带参主函数main中的argv指针数组后示意见图9-4。图9-4 带参主函数main中的argv示意例如,在c盘根目录下的文件file1.c的内容如下:#includevoid main(int argc,char*argv)while(argc1)+argv;printf(%sn,*argv);argc-;用VC+6.0中的工具栏Build中的Build命令先将file1.c生成可执行文件file1.exe(保存于Debug子目录中),其后再将file1.exe由Debug目录移至c盘根目录下,然后点击桌面上“开始”
展开阅读全文