《新编C语言程序设计教程PPT第9章指针课件.ppt》由会员分享,可在线阅读,更多相关《新编C语言程序设计教程PPT第9章指针课件.ppt(39页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、新编C语言程序设计教程 清华大学出版社周二强 软件学院 计算机科学与工程系配套视频:配套视频: 博客:博客:第第9 9章章 指针指针9.6 main9.6 main函数和命令行参数函数和命令行参数9.7 9.7 指向函数的指针变量指向函数的指针变量 9.8 9.8 使用堆空间使用堆空间9.9 9.9 典型例题典型例题9.6 main9.6 main函数和命令行参数函数和命令行参数在前面章节中为了简便在前面章节中为了简便mainmain函数都没有返回值,实际上函数都没有返回值,实际上mainmain函数标准的定函数标准的定义形式为:义形式为:int main(void)int main(void
2、)或或int main(int argc,char*argv)int main(int argc,char*argv)。当程序不需要使用命行参数时使用第一种形式,需要命令行参数时使用第当程序不需要使用命行参数时使用第一种形式,需要命令行参数时使用第二种形式。二种形式。命令行参数命令行参数是指以命令行方式运行程序时所带的参数。设工程编译命令行参数是指以命令行方式运行程序时所带的参数。设工程编译后得到了一个名为后得到了一个名为test.exetest.exe的可执行文件,则以命令行方式运行程序的可执行文件,则以命令行方式运行程序的方法为:先启动的方法为:先启动“DOS”“DOS”窗口(开始窗口(开
3、始运行运行输入输入cmdcmd回车或开始回车或开始程序程序附件附件命令提示符),再把当前目录转到工程所在目录的命令提示符),再把当前目录转到工程所在目录的debugdebug子目录(如子目录(如E:csampletestdebugE:csampletestdebug),然后输入),然后输入testtest回车运行回车运行程序。程序。命令行参数输入输入test a b cdtest a b cd回车回车输入被空格分成了四个字符串,系统会把这些字符串传给输入被空格分成了四个字符串,系统会把这些字符串传给mainmain函数。函数。如果如果main main 函数用第二种形式定义,则当程序运行时参数
4、函数用第二种形式定义,则当程序运行时参数argcargc的值是的值是命令行中字符串的个数,此时命令行中字符串的个数,此时argcargc的值为的值为4 4。命令行中的每个字符串。命令行中的每个字符串都被存储到内存中,并且都被存储到内存中,并且*argvargv(即(即argv0argv0)指向第一个字符串,即)指向第一个字符串,即文件名(文件名(texttext),),*(argv+1)(argv+1)(即(即argv1argv1)指向第二个字符串)指向第二个字符串(aa),以此类推。除文件名之外的字符串),以此类推。除文件名之外的字符串aa、bb、cdcd就是所就是所谓的命令行参数。谓的命令
5、行参数。命令行参数int main(int argc,char*argv)int main(int argc,char*argv)例9-23 请分析下面的程序 库函数库函数atoiatoiint atoi(const char*string)int atoi(const char*string)把把stringstring指向的由数字构成的字指向的由数字构成的字符串转换成相应的整数。符串转换成相应的整数。例9-23 程序的运行例9-23 请分析下面的程序 注意:1.1.操作系统会获得操作系统会获得mainmain函数的返回值,函数的返回值,mainmain函数的返回值为函数的返回值为0 0时表
6、时表示程序运行顺利,正常退出。示程序运行顺利,正常退出。2.2.在在mainmain函数的第二种定义形式中,参数类型固定,但参数名可变。函数的第二种定义形式中,参数类型固定,但参数名可变。如也可以用如下形式定义如也可以用如下形式定义mainmain函数。函数。int main(int n,char*pp)int main(int n,char*pp)return9.7 9.7 指向函数的指针变量指向函数的指针变量存放函数体中相关指令的存储单元通常位于内存中称为代码段的部分。与数存放函数体中相关指令的存储单元通常位于内存中称为代码段的部分。与数组名类似,函数名的值在组名类似,函数名的值在C C语
7、言中同样被规定为与该函数相关的存储单元的首语言中同样被规定为与该函数相关的存储单元的首地址。地址。调用执行函数实际上就是执行从函数名标识的首地址开始的相关存储单元中调用执行函数实际上就是执行从函数名标识的首地址开始的相关存储单元中的指令。如果一个指针变量可以用函数名赋值,则称该指针变量为指向函数的指令。如果一个指针变量可以用函数名赋值,则称该指针变量为指向函数的指针变量。的指针变量。利用指向函数的指针变量也可以获得与函数相关的存储单元的地址,有了这利用指向函数的指针变量也可以获得与函数相关的存储单元的地址,有了这个地址就能够执行相关指令,也这就是说利用指向函数的指针变量也可以调个地址就能够执行
8、相关指令,也这就是说利用指向函数的指针变量也可以调用执行函数,与使用函数名调用执行函数类似。用执行函数,与使用函数名调用执行函数类似。如何定义指向函数的指针变量呢?指针变量的定义用指针变量的定义用*号,函数的定义用一对圆括号。号,函数的定义用一对圆括号。函数的作用是完成从输入到输出的转换,编译系统检查函数调用正确与否的函数的作用是完成从输入到输出的转换,编译系统检查函数调用正确与否的关键在于实参的个数、类型是否匹配、返回值类型是否匹配。综上所述,在关键在于实参的个数、类型是否匹配、返回值类型是否匹配。综上所述,在定义指向函数的指针变量时需体现以上诸多要素。定义指向函数的指针变量时需体现以上诸多
9、要素。简单地说,定义时只需在函数的首部中把函数名部分改为简单地说,定义时只需在函数的首部中把函数名部分改为(*(*指针变量名指针变量名),并省略形参名即可。如求两个整数和的函数的说明为并省略形参名即可。如求两个整数和的函数的说明为int add(int m,int int add(int m,int n);n);,则指向此函数的指针变量则指向此函数的指针变量pfpf的定义为的定义为int(*pf)(int,int);int(*pf)(int,int);。注意:1.1.语句语句int*pf(int,int);int*pf(int,int);为函数为函数pfpf的声明,该函数有两个整型的的声明,该
10、函数有两个整型的形参,返回值类型为指向整型变量的指针。形参,返回值类型为指向整型变量的指针。2.2.指向函数的指针变量指向函数的指针变量pfpf的值可以是一类函数的首地址,这类函数的值可以是一类函数的首地址,这类函数的特点是有两个整型形参,返回值类型也为整型。的特点是有两个整型形参,返回值类型也为整型。int add2(int x,int add2(int x,int y)pf=add2;int y)pf=add2;例9-24 使用指向函数的指针变量调用函数例9-25 利用梯形法求f(x)的定积分的公式为例9-25 利用梯形法求f(x)的定积分return9.8 9.8 使用堆空间使用堆空间存
11、放程序中数据的内存通常分为两个区:静态存储区和动态存储区。存放程序中数据的内存通常分为两个区:静态存储区和动态存储区。与全局变量相关的存储单元位于静态存储区,它们在程序运行之前与全局变量相关的存储单元位于静态存储区,它们在程序运行之前分配,在程序运行期间始终为程序所有。分配,在程序运行期间始终为程序所有。与局部变量相关的存储单元位于动态存储区的栈中,它们在程序运与局部变量相关的存储单元位于动态存储区的栈中,它们在程序运行期间定义时分配,超出作用域后释放。行期间定义时分配,超出作用域后释放。动态存储区中还有一种称为堆的存储空间,在程序运行期间可以根动态存储区中还有一种称为堆的存储空间,在程序运行
12、期间可以根据需要利用库函数在其上分配一块内存。据需要利用库函数在其上分配一块内存。栈和堆栈和堆是动态存储区中的两类存储空间。栈和堆是动态存储区中的两类存储空间。栈和堆中的存储单元都可以在程序运行期间分配或释放,但两者的栈和堆中的存储单元都可以在程序运行期间分配或释放,但两者的管理方式不同。管理方式不同。栈空间中存储单元由系统自动地分配和释放,而堆空间中的存储单栈空间中存储单元由系统自动地分配和释放,而堆空间中的存储单元必须由程序员调用相关的库函数显式地分配和释放。元必须由程序员调用相关的库函数显式地分配和释放。如果程序中申请的位于堆空间中的存储单元在使用完毕后没有显式如果程序中申请的位于堆空间
13、中的存储单元在使用完毕后没有显式地释放,则它会一直为程序所拥有,直至程序运行结束。地释放,则它会一直为程序所拥有,直至程序运行结束。申请堆空间库函数库函数mallocmalloc用于在堆空间中申请一块存储空间,它的形参是一个用于在堆空间中申请一块存储空间,它的形参是一个无符号整型,指出需分配内存块的以字节为单位的长度。无符号整型,指出需分配内存块的以字节为单位的长度。如果内存块分配成功,则如果内存块分配成功,则mallocmalloc函数返回该内存块的首地址,否则函数返回该内存块的首地址,否则它将返回空指针它将返回空指针NULLNULL。显然,需要使用指针变量接受显然,需要使用指针变量接受ma
14、llocmalloc函数返回的地址,但是函数返回的地址,但是mallocmalloc函数分配的内存块是什么类型呢?函数分配的内存块是什么类型呢?malloc函数的返回值类型mallocmalloc函数仅仅返回一个地址连续的内存块的首地址,该内存块在分配时函数仅仅返回一个地址连续的内存块的首地址,该内存块在分配时没有指定类型。没有指定类型。例如函数调用例如函数调用malloc(8)malloc(8)可能返回一个地址可能返回一个地址0 x0044 02b00 x0044 02b0,但实际上与该地,但实际上与该地址相关的内存块从址相关的内存块从0 x0044 02b00 x0044 02b0至至0
15、x0044 02b70 x0044 02b7共共8 8个字节。个字节。如果此内存块用来存储整数,则它可以存储两个整数,此时内存块的类型如果此内存块用来存储整数,则它可以存储两个整数,此时内存块的类型像是一个有像是一个有2 2个数组元素的整型数组。如果这块内存用来存储双精度的浮点个数组元素的整型数组。如果这块内存用来存储双精度的浮点数,则它可以存储一个双精度的浮点数,此时该内存块的类型是双精度的数,则它可以存储一个双精度的浮点数,此时该内存块的类型是双精度的浮点型。浮点型。mallocmalloc函数的返回值类型是函数的返回值类型是void*void*。void*在在C C语言中语言中void*
16、void*表示指向表示指向voidvoid型存储单元的指针。型存储单元的指针。voidvoid型的存储单元是型的存储单元是“无类型无类型”的存储单元,仅是一个地址连续的内的存储单元,仅是一个地址连续的内存块。无类型的内存块可以强制转换为其它类型的存储单元。存块。无类型的内存块可以强制转换为其它类型的存储单元。如果想用如果想用mallocmalloc函数分配的内存块存储整数,则使用方式为:函数分配的内存块存储整数,则使用方式为:int*pi int*pi=(int*)malloc(8);*pi=2;*(pi+1)=3;=(int*)malloc(8);*pi=2;*(pi+1)=3;。如果想用分
17、配的内存块存储双精度的浮点数,则使用方式为:如果想用分配的内存块存储双精度的浮点数,则使用方式为:double double*pf=(double*)malloc(8);*pf=2.3;*pf=(double*)malloc(8);*pf=2.3;注意:1.1.可以定义一个可以定义一个voidvoid型的指针变量,其它类型的指针变量无需类型型的指针变量,其它类型的指针变量无需类型转换就可以直接给它赋值,但是,转换就可以直接给它赋值,但是,voidvoid型的指针变量在使用时必须强型的指针变量在使用时必须强制转换为其它类型的指针变量。制转换为其它类型的指针变量。2.malloc2.malloc函
18、数常见的使用方式为函数常见的使用方式为int int*pi=(int*)malloc(2*sizeof(int);*pi=(int*)malloc(2*sizeof(int);。库函数库函数freefree用于释放使用用于释放使用mallocmalloc函数申请的堆空间,函数申请的堆空间,freefree函数的首部函数的首部为为void free(void*memblock)void free(void*memblock)。使用。使用freefree函数释放堆空间时只需把内函数释放堆空间时只需把内存块的首地址传给存块的首地址传给freefree函数即可函数即可,无需考虑它已被强制转换为何种类型
19、无需考虑它已被强制转换为何种类型了。了。例9-26 分析下面的程序注意:1.1.在在VC6.0VC6.0中使用库函数中使用库函数mallocmalloc和和freefree时需要包含头文件时需要包含头文件stdlib.hstdlib.h或头文件或头文件malloc.hmalloc.h。2.2.堆空间使用完毕必须及时释放以防内存泄露,释放完毕后需把相堆空间使用完毕必须及时释放以防内存泄露,释放完毕后需把相关指针变量赋值成空指针以防止出现野指针。关指针变量赋值成空指针以防止出现野指针。内存泄露内存泄露指由于疏忽或错误未能释放已经不再使用的堆空间内存。内存泄露指由于疏忽或错误未能释放已经不再使用的堆
20、空间内存。当程序中申请的一块堆空间内存没有指针变量指向它时,这块内存当程序中申请的一块堆空间内存没有指针变量指向它时,这块内存肯定会因无法释放而泄露了。肯定会因无法释放而泄露了。为所有程序共享的堆空间容量有限,当一个程序因内存泄露占用了为所有程序共享的堆空间容量有限,当一个程序因内存泄露占用了大量的堆空间时,其它程序可能会因申请不到堆空间而不能正常运行。大量的堆空间时,其它程序可能会因申请不到堆空间而不能正常运行。例9-27 分析下面的函数该函数在调用执行时会发生内存泄露。指针变量该函数在调用执行时会发生内存泄露。指针变量strstr是局部变量,函数执行是局部变量,函数执行完毕其存储单元会自动
21、释放,此时函数中在堆上申请的内存块将没有指针变完毕其存储单元会自动释放,此时函数中在堆上申请的内存块将没有指针变量指向它,也就是说在程序中将无法释放此块内存,此内存块量指向它,也就是说在程序中将无法释放此块内存,此内存块“泄露泄露”了。了。每调用该函数一次,就泄露一块内存。每调用该函数一次,就泄露一块内存。return9.9 9.9 典型例题典型例题9-28 9-28 已知已知int a32,i,j;int a32,i,j;,且有,且有0 i 3,0 j 20 i 3,0 j 2。1.1.写出几个表示数组元素写出几个表示数组元素aijaij所标示存储单元的地址的表达式;所标示存储单元的地址的表
22、达式;分析分析&aii&aii。*(a+1)+1*(a+1)+1表示表示a11a11的地址,显然的地址,显然*(a+i)+j*(a+i)+j表示表示aijaij的地址。的地址。ai+jai+j又由于又由于aijaij与与a00a00相距相距i*2+ji*2+j个存储单位,所以个存储单位,所以aijaij的地址也的地址也可表示为可表示为&a00+i*2+j&a00+i*2+j。9.9 9.9 典型例题典型例题9-28 9-28 已知已知int a32,i,j;int a32,i,j;,且有,且有0 i 3,0 j 20 i 3,0 j 0m0)的报数,报到)的报数,报到m m的人出圈;的人出圈;
23、接着从下一个人继续接着从下一个人继续1 1至至m m的报数,报到的报数,报到m m的人出圈;一直进行这样的的人出圈;一直进行这样的报数,直到所有的人都出圈为止,试问他们出圈的次序。报数,直到所有的人都出圈为止,试问他们出圈的次序。9-30第一步,判断当前要报数的人是否已经出圈,如果没有出圈则先报数(用第一步,判断当前要报数的人是否已经出圈,如果没有出圈则先报数(用+k+k表示),再判断报的数是否为表示),再判断报的数是否为m m,如果是,则输出报数人的编号后让其出,如果是,则输出报数人的编号后让其出圈(此时变量圈(此时变量k k的值需清零,表示出圈人数的变量的值需清零,表示出圈人数的变量g g
24、的值需加的值需加1 1)。)。第二步,调整指针变量第二步,调整指针变量p p指向下一个数组元素。指向下一个数组元素。重复上面两步,直到变量重复上面两步,直到变量g g的值为的值为n n时停止。时停止。9-309-31 库函数qsort库函数库函数qsortqsort在头文件在头文件stdlib.hstdlib.h中声明为中声明为void qsort(void*base,unsigned void qsort(void*base,unsigned n,unsigned size,int(*fcmp)(const void*,const void*)n,unsigned size,int(*fcm
25、p)(const void*,const void*)。库函数。库函数qsortqsort采用快速排序算法对从采用快速排序算法对从basebase起始的起始的n n个元素个元素base0base0到到basen-1basen-1进行排进行排序。序。参数参数basebase的类型为何定义为的类型为何定义为void*void*?排序时肯定要比较两元素的大小,数组元素的类型不知道怎么比较大小?排序时肯定要比较两元素的大小,数组元素的类型不知道怎么比较大小?参数参数unsigned sizeunsigned size为为basebase中每个元素占用的存储单元的字节数。中每个元素占用的存储单元的字节数
26、。库函数qsort分析void qsort(void*base,unsigned n,unsigned size,int(*fcmp)(const void qsort(void*base,unsigned n,unsigned size,int(*fcmp)(const void*,const void*)void*,const void*)排序后各元素将依照比较函数排序后各元素将依照比较函数fcmpfcmp的定义升序排列。函数指针的定义升序排列。函数指针fcmpfcmp指向的是指向的是一个用来比较两个参数一个用来比较两个参数“大小大小”的函数。若第一个参数的函数。若第一个参数“小于小于”第
27、二个参数,第二个参数,它通常返回一个小于它通常返回一个小于0 0的整数;否则若二者相等,则通常返回的整数;否则若二者相等,则通常返回0 0;否则返回通;否则返回通常一个大于常一个大于0 0的整数。的整数。9-31调用qsort函数对一个整型数组排序。分析:分析:使用使用qsortqsort函数需要定义一个比较整数大小的函数,这个比较函数的函数需要定义一个比较整数大小的函数,这个比较函数的参数应符合函数指针参数应符合函数指针fcmpfcmp的要求,定义时需注意形参的类型及的要求,定义时需注意形参的类型及void*void*型参数的使用。型参数的使用。9-32调用qsort函数对下面数组中的字符串常量排序。char*str =Henan,Beijing,Guangzhou;char*str =Henan,Beijing,Guangzhou;9-32注意:程序执行后程序执行后strstr数组的状态变化可能如图数组的状态变化可能如图9-139-13所示。所示。char*字符?字符串?return