《C语言程序设计》课件第6章_第1页
《C语言程序设计》课件第6章_第2页
《C语言程序设计》课件第6章_第3页
《C语言程序设计》课件第6章_第4页
《C语言程序设计》课件第6章_第5页
已阅读5页,还剩48页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

目录函数概述6.1函数的定义与调用6.2函数间的信息交换6.3函数的嵌套调用和递归调用6.4变量的作用域和存储类型6.5外部函数与内部函数6.6多文件程序的运行6.7目录函数概述6.16.1函数概述说明

函数用来组成源程序文件,而C程序由源程序文件组成,函数之间彼此平行,定义时相互独立,但执行时可能会交叉执行。目的

分而治之,化繁为简;

减少代码重复性;

使代码模块化,容易阅读和维护;分类

库函数;用户自定义函数;目录函数的定义与调用6.26.2.1函数的定义函数必须限定以后使用。定义语法如下:返回值类型

函数名(类型参数1,类型参数2,……) {

函数体; //return表达式; }若有return语句,当执行到return时,就终止函数的执行,返回到调用它的地方。6.2.2函数的调用函数的调用形式:

函数名(实参列表);有返回值时

函数调用可以放到一个数值表达式中

如 c=max(a,b);

也可以作为另一个函数调用的参数 c=max(max(a,b),c);无返回值时

函数调用可以直接作为表达式

如 display(a,b);6.2.3函数的声明调用函数之前,先要对其返回值类型、函数名和参数进行声明,有助于编译器进行类型检查。声明时不要省略参数以及返回值类型。当返回值为整型或者函数定义在函数调用前面时,可以省略原型规定:原型一定要先定义或声明阅读代码,分析编译错误的原因。#include<stdio.h>intmain(){doublem,n,max;scanf("%d%d",&m,&n);max=getMax(m,n); printf("%lf\n",max); return0;}doublegetMax(doublex,doubley){if(x-y>1e-6)returnx;elsereturny;}目录函数间的信息交换6.3intAverage(intx,inty){intresult;result=(x+y)/2;returnresult;}intmain(){inta=12;intb=24;intave;ave=Average(a,b);

printf(……);

return0;}数据传递执行顺序6.3.1函数的参数函数参数:

形参:在定义函数时,定义函数名后面括号中的变量名

实参:主调函数中调用函数,调用函数名后面括号中的参数实参形参6.3.2数组作为函数参数数组元素作函数实参

数组元素可作实参但不能作形参。因为形参是临时被分

配存储单元的,而数组是内存中一段连续的存储单元,不可能

为一个数组元素单独分配存储单元。求数组内十个元素的最大值并指出其位置所在。#include<stdio.h>intmax(intx,inty);intmain(){inta[10],m,n,i,k;printf("请输入10个整数:");for(i=0;i<10;i++)scanf("%d",&a[i]);for(i=1,n=0,m=a[0];i<10;i++){k=max(m,a[i]); if(k>m){m=k;n=i;}}printf("最大数是:%d,其在第%d位。\n",m,n+1); return0;}intmax(intx,inty){return(x>y?x:y);}6.3.2数组作为函数参数一维数组名作函数参数

数组名作实参形参都可以。数组名作函数实参时,向形参传递的是数组元素的首地址。求十个学生的平均成绩。在主函数中定义一个实型数组score,将输入的10个学生成绩存放在数组中。average函数用来求学生的平均成绩。这样就需要把数组有关的信息传递给average函数。用数组名作为实参,把数组地址传给average函数,在该函数中对数组进行处理。#include<stdio.h>floataverage(floatarray[10]){ inti; floataver,sum=array[0]; for(i=0;i<10;i++) sum=sum+array[i]; aver=sum/10; return(aver);}intmain(){ floatscore[10],aver; inti; printf("请输入10个成绩:"); for(i=0;i<10;i++) scanf("%f",&score[i]); aver=average(score);//以数组名为实参调用average函数 printf("平均成绩:%.2f\n",aver); return0;}6.3.2数组作为函数参数多维数组名作函数参数

多维数组元素可同一维数组元素一样作函数实参,多维数组名可做函数实参和形参。求3*3矩阵中对角线元素之和。#include<stdio.h>intadd(intb[][3],intn);intmain(){inta[3][3]={{1,3,4},{2,4,5},{6,7,4}};inti,j,s;printf("矩阵:\n");for(i=0;i<3;i++){for(j=0;j<3;j++)printf("%4d",a[i][j]);printf("\n");}s=add(a,3);//实参是二维数组名printf("矩阵的对角线上的元素之和为:%d\n",s); return0;}intadd(intb[][3],intn){inti,j,s=0;for(i=0;i<n;i++)for(j=0;j<n;j++) if(i==j)//对角线元素行号和列号相等s+=b[i][j];returns;}6.3.3参数传递方式值传递传递的是实际的值,实参和形参占不同的存储单元且单项值传递。调用结束后,形参的存储单元被释放。void

swap(intx,inty){inttmp;

tmp=x;x=y;y=tmp;

printf(“形参x=%d,y=%d\n”,a,b);}intmain(){inta=10;intb=20;

swap(a,b);

printf(“实参a=%d,b=%d\n”,a,b);return0;}数据传递执行顺序形参实参6.3.3参数传递方式地址传递传递的是地址,形参收到的也是指向实参存储单元的地址。调用结束后,实参随形参的变化而变化。void

swap(intx[2]){inttmp;

tmp=x[0];x[0]=x[1];x[1]=tmp;

printf(“形参x[0]=%d,x[1]=%d\n”,a,b);}intmain(){inta[2]={10,20};

swap(a);

printf(“实参a[0]=%d,a[1]=%d\n”,a,b);return0;}数据传递执行顺序形参实参a[0]10a[1]200x36246.3.4函数返回值的实现若函数需要返回值,返回值表达形式为: return(表达式);intAverage(intx,inty){intresult;result=(x+y)/2;returnresult;}intmain(){inta=12;intb=24;intave;ave=Average(a,b);

printf(……);

return0;}数据传递执行顺序实参形参目录函数的嵌套调用和递归调用6.46.4.1函数的嵌套调用在C语言中,函数的定义是互相独立平行的,不可嵌套定义,但是可以在被调函数中又调用函数,这就是嵌套调用。main(){……a();}a

函数{b();…return;}b函数{……return;}①③④⑤⑥⑦②6.4.1函数的嵌套调用求两整数的最大公约数和最小公倍数。#include<stdio.h>#include<math.h>intgcd(inta,intb){ intc; if(a<b)//保证a的值大于b { c=b; b=a; a=c; } while(a!=0) { c=a%b; b=a; a=c;//把余数赋值给a,直到a=0时跳出循环,找到结果。

} returnb;}intlcd(inta,intb){ intc; c=(a*b)/(gcd(a,b));//函数嵌套的过程

returnc;}intmain(){ intm,n; printf("请输入两个数:"); scanf("%d,%d",&m,&n); printf("%d和%d最大公约数为:%d\n",m,n,gcd(m,n)); printf("最小公倍数为:%d\n",lcd(m,n));return0;}6.4.2函数的递归调用

(1)递归调用的内容函数的递归调用就是一个函数在他的函数体内,直接或者间接的调用了它本身。①

直接递归调用:函数体内直接调用自身,

示例代码段如下:voidtest1(){

…… test1();

……}直接递归调用示意图如图所示:6.4.2函数的递归调用

间接递归调用:函数体间接调用自身,示例代码段如下:voidtest1(){

…… test2();

……}voidtest2(){

…… test1();

……}6.4.2函数的递归调用

间接递归调用示意图所示:以上两种递归调用在没有限制条件的情况下都是无限循环调用。为了使程序合理化的解决问题,使递归调用有限进行,在使用递归调用时必须在函数内加条件判断,满足条件后就不再做递归调用,然后逐层返回。6.4.2函数的递归调用

(2)递归调用的条件采用递归方法来解决问题,必须满足以下条件:①

可以通过递归调用来缩小问题规模,且新问题与原问题有着相同的形式,这样就可以利用递归把问题分解成为规模更小的、具有与原问题有着相同解法的问题;②

存在一种简单情境,可以使递归在简单情境下退出。6.4.2函数的递归调用

【例6.12】利用递归调用求费波纳契数列的第n项。#include<stdio.h>longfib(intn){if(n==0)return0;elseif(n==1)return1;elsereturnfib(n-1)+fib(n-2);}intmain(){intn;printf("请输入n:");scanf("%d",&n);printf("费波纳契函数的第%d项值为:%ld\n",n,fib(n));return0;}程序运行结果如图所示:在编写递归调用的函数的时候,一定要把对简单情境的判断写在最前面,以保证函数调用在检查到简单情境的时候能够及时地中止递归,否则,你的递归函数可能会永不停息的在那里递归调用了。6.4.2函数的递归调用

(3)递归调用的优缺点:优点:①

代码简洁②

便于理解缺点:①

时间和空间的消耗比较大②

重复计算③

调用会使内存溢出6.4.2函数的递归调用

【例6.13】利用递归调用求n!。#include<stdio.h>longfactorial(intn){if(n==1)return1;elsereturnn*factorial(n-1);}intmain(){intn;printf("请输入n:");scanf("%d",&n);printf("%d!=%ld\n",n,factorial(n));return0;}程序运行结果如图所示:目录变量的作用域和存储类型6.56.5.1变量的作用域变量定义的位置有以下几种情况:①

在所有函数外部定义②

在头文件中定义③

在函数或语句块内部定义④

函数的参数根据变量定义的位置的不同,可将变量分为局部变量和全局变量两种类型。6.5.1变量的作用域(1)局部变量定义在函数体内、语句块内部和函数参数的变量,称为局部变量。在函数体内部定义的变量只能在本函数范围内有效,在此函数之外是不能使用这些变量的。在复合语句块中定义的变量只能在本复合语句块中使用。例如:1intfunc()2{3 inta,b,t;4 {5 intc;6 c=a+b;7 }8 t=a*b;9 returnt;10}该程序段第3行定义的变量a、b和t,只能在func()函数体内使用,即第2~9行,在其他地方无法使用。第5行定义的变量c只在复合语句块内有效,即第4~6,离开此复合语句块就无效了,系统会把它占用的内存单元释放。6.5.1变量的作用域注意:①

即使是主函数中定义的变量,也只是在主函数内部有效。②

不同函数中的变量可同名。它们之间互相独立平行,互不干扰。如例题6.13,main()函数中有一个局部变量n,factorial()函数的形参n也是局部变量。两个局部变量虽然同名,但属于不同的局部代码区域,所以相互并无影响。6.5.1变量的作用域(2)全局变量在函数体内定义的变量是局部变量,而在函数之外定义的变量称为全局变量。全局变量可以为本文件中其他函数共用,其有效范围从定义的那一行开始到本文件结束。【例6.15】阅读以下段代码,局部变量与外部变量同名,分析结果。#include<stdio.h>//此处a和b是全局变量inta=3;intb=5;intmax(inta,intb);intmain(){//此处a是局部变量inta=8;printf("max=%d\n",max(a,b));return0;}//此处形参a,b,c是局部变量intmax(inta,intb){intc;c=a>b?a:b;returnc;}首先全局变量给a=3、b=5赋初值,程序执行时从main()函数开始,此时局部变量a有效,给a赋值8,调用max函数,实参向形参进行值传递,将8、5传给max()函数的形参a、b,经过比较后,max返回值是8,故结果为8而不是5。当局部变量与全局变量同名时,可以根据就近原则进行选择使用。6.5.1变量的作用域注意:全局变量虽然作用域大,但是不必要的时候不建议使用全局变量,原因如下:①

全局变量在程序开始到结束这个过程中一直占用内存单元,而不像局部变量一样需要时才临时开辟存储单元;②

变量通用性较低,可移植性较低。因为全局变量一旦使用就会影响这个函数整体,若需要将这个函数移植到别的文件中,那其全局变量也要一起移植,这样就会有变量同名的问题出现,就会降低程序的通用性。编写代码使遵循的原则是,模块功能要单一,模块之间的相互影响要降到最低,因此全局变量的使用不符合此原则。6.5.2变量的生命周期变量除了从作用域的角度分类,分为全局和局部变量之外,还可以从占用内存的时间上来观察,即观察变量在内存中的生存期。变量占用内存的时间长度,称为变量的生命周期。(1)局部变量的生命周期①

定义在函数体内和函数形参的局部变量,其生命周期是从函数调用开始到函数调用结束。②

定义在复合语句块中的局部变量,其生命周期是复合语句块左括号{开始,到复合语句块右括号}结束。(2)全局变量的生命周期全局变量的生命周期是从程序运行开始,到程序运行结束,也称为全周期变量。6.5.3变量的存储类型根据变量的生命周期可知,有的变量从程序开始到结束是一直存在的,而有的变量只是在调用函数时才临时分配存储单元,函数调用结束后存储单元就会被释放。由此得来,变量的存储有静态存储和动态存储两种方式。静态存储方式是指在程序运行期间由系统分配固定的存储空间的方式,而动态存储方式则是在程序运行期间根据需要进行动态的分配存储空间的方式。6.5.3变量的存储类型存储空间可以分为三部分:①

程序区;②

静态存储区;

动态存储区。而在动态存储区内存放的数据有:①

函数形参,调用时动态分配存储空间;②

函数体中没有用static声明的变量;

函数调用时的现场保护和返回地址等。6.5.3变量的存储类型根据变量存放在内存的区域的不同,可以将变量归为不同的存储类型。(1)局部变量的存储类型①

自动型变量(auto变量)定义局部变量时,如果没有明变量的存储类别,都是默认为自动型变量。自动型局部变量的数据存储在内存的动态存储区。函数中的形参和定义在函数中的局部变量都属于这一类,因为在函数调用和结束会发生动态的存储和释放存储空间,所以被称为自动型变量。用关键字auto声明,或者省略不写,会被默认为自动变量。例如:inta(intb){ ..... autointc=3; .....}其中b是形参,c是自动变量,执行完函数a后,自动释放变量b和c的存储空间。且与auto省略不写效果相同。6.5.3变量的存储类型②

静态局部变量(static变量)有时程序的功能需求会要求函数中的局部变量在调用结束后继续保留原值,而且其占用的存储空间不被释放,下次调用时变量还是上次调用结束后的值,这是就需要用static关键字对变量进行声明,用static关键字修饰的局部变量称为静态变量。【例6.16】求2-5的阶乘。#include<stdio.h>intmain(){intfac(intn);inti=1;for(i=2;i<=5;i++)printf("%d!=%d\n",i,fac(i));return0;}intfac(intn){staticintf=1;f=f*n;return(f);}静态局部变量在编译时进行赋初值,只赋值一次,在程序正式运行时就已经有初值了,若没有初始化,则为默认值0,这点跟全局变量很类似,因此静态局部变量具有全局性。而动态变量的赋初值是在函数调用的时候进行的,每调用一次函数重新给一次初值。6.5.3变量的存储类型③

寄存器量(register变量)一般变量是存放在内存中的,当程序用到哪一个变量的值时,控制器发出指令将内存中的变量值送到运算器;如果需要保存数据,再从运算器中将数据送回内存。运算器与内存之间的数据传递,是需要消耗时间的,若有些变量频繁使用,则为了提高效率,允许将变量的值放在CPU的寄存器中,需要时直接从寄存器中取出,这种变量就叫做寄存器变量,用关键字register进行声明。现在一般比较少用。6.5.3变量的存储类型(2)全局变量的存储类型全局变量都是存放在静态存储区中的,因此它们的生命期存在于程序执行的整个过程。而且一般来说它的作用域是从变量定义处到本程序文件的结尾。但有时需要对全局变量进行作用域的扩展。①

在文件内扩展全局变量的作用域

将全局变量的作用域扩展到其他文件【例6.17】给定b的值,输入a和m,求a*b和a的m次幂的值。文件test1.cpp:#include<stdio.h>#include<string.h>intpower(int);inta;intmain(){ intb=3,c,d,m; printf("enterthenumberaanditspowerm:\n"); scanf("%d,%d",&a,&m); c=a*b; printf("%d*%d=%d\n",a,b,c); d=power(m); printf("%d**%d=%d\n",a,m,d); return0;}文件test2.cpp://把test1文件中的全局变量a的作用域扩展到本文件中externinta;intpower(intn){ inti,y=1; for(i=1;i<=n;i++) y*=a; returny;}6.5.3变量的存储类型注意:不要误以为对全局变量加static声明后才采取静态存储方式,而不加static的是采用动态存储方式。声明局部变量的存储类型和声明全局变量的存储类型的含义是不同的。对局部变量来说,声明存储类型的作用是指定变量的存储的区域(静态存储区或者动态存储区)以及由此产生的生命周期问题。而对于全局变量来说,由于都是在编译时分配内存,都存放在静态存储区,故声明存储类型的作用是扩展变量作用域问题。目录外部函数与内部函数6.6根据函数能否被其他源文件调用分为内部函数和外部函数。6.6.1外部函数

在定义函数时,若在函数的存储类型声明为关键字extern,则此函数就是外部函数,可被其他源文件调用。例如:externintprintName(charstr[]);

这样函数PrintName就能被其他文件调用。C语言规定,若在定义函数时省略extern,则默认为外部函数,本书中前面所有函数都默认是外部函数。

在需要调用此函数的文件中,也需要对此函数做声明,在声明时要加关键字extern,表示此函数是在其他文件中定义的外部函数。6.6.1外部函数

【例6.18】输入一行字符串,并删除想要删除的字符串中的字段。文件test11.cpp代码#include<stdio.h>intmain(){//以下3行声明在main中将要调用,在其他文件中定义的3个函数externvoidenterString(charstr[]);externvoiddeleteString(charstr[],charch);externvoidprintString(charstr[]);charc;charstr[80];printf("请输入一个字符串:");enterString(str);printf("请输入要删除的字符:");scanf("%c",&c);deleteString(str,c);printString(str);return0;}文件test12.cpp代码#include<stdio.h>//定义外部函数enterString()voidenterString(charstr[80]){gets(str);//向字符数组输入字符串}文件test13.cpp代码//定义外部函数deleteString()voiddeleteString(charstr[],charch){inti,j;for(i=j=0;str[i]!='\0';i++)if(str[i]!=ch)str[j++]=str[i];str[j]='\0';}文件test14.cpp代码#include<stdio.h>voidprintString(charstr[]){printf("%s\n",str);}6.6.2内部函数

若函数只能被本文件中的其他函数调用,则该函数被称为内部函数。在定义内部函数时,函数的存储类型使用static关键字,即:static函数类型函数名(形参列表);例如:staticintprint_name(charstr[]);通常在编写代码时,习惯把只作用于本文件的内部函数和全局变量放在文件的开头,并加static使之局部化,提高程序的可靠性。6.6.2内部函数

【例6.19】继续使用例6.18的代码,将test14.cpp文件中printString()函数定义为静态函数,则该函数只在test14.cpp中有效,当执行程序时,IDE会报错。工程目录文件和错误提示如图所示。目录多文件程序的运行6.76.7多文件程序的运行

一个函数只能定义在一个文件中,但一个文件可以有多个函数。首先是头文件和源文件,头文件中包含一些类型的定义、结构体定义、宏定义、函数声明、#include包含等内容,在源文件中编写实际的功能实现。而使用#include""来包含的头文件,编译器将会从当前程序目录进行查找。在#include时,被包含文件可以是绝对路径,也可以是相对路径,总之,只要头文件的存放路径与当前源文件的关系正确即可。6.7多文件程序的运行

多文件编程,编写步骤如下:①

把所有函数分散在多个文件中,通常主函数在单独的文件里。②

为每个源文件编写一个配对的以.h作为扩展名的头文件,不分配内存的内容都可以写在头文件里,头文件里至少要包含配对源

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论