第3章(3.2-程序模块-函数)_第1页
第3章(3.2-程序模块-函数)_第2页
第3章(3.2-程序模块-函数)_第3页
第3章(3.2-程序模块-函数)_第4页
第3章(3.2-程序模块-函数)_第5页
已阅读5页,还剩75页未读 继续免费阅读

下载本文档

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

文档简介

3.2程序模块——函数3.2.1模块的引入3.2.2函数的执行3.2.3链接与建库3.2.4进一步认识变量

1/803.2.1模块的引入★程序一般由一个或若干个相对独立的模块组成,

C语言把模块称为函数。★有一个程序必不可少的函数,它是程序运行的起点函数,也就是主函数main()

。★一个C程序由一个主函数和若干个其他函数构成。2/80★由主函数调用其他函数,其他函数也可以互相调用。同一个函数可以被一个或多个函数调用任意多次。★子函数必需通过别的函数调用才能得到运行。3/80★所有函数都是平行的,即在定义函数时分别进行,是互相独立的。一个函数并不从属于另一函数,即函数不能嵌套定义。函数间可以互相调用,但不能调用main函数。main函数是由系统调用的。

★C程序的执行从main函数开始,若在main函数中调用其他函数,在调用后流程返回到main函数,

在main函数中结束整个程序的运行。4/801.函数的分类●标准函数(库函数):由系统提供,用户不必自己定义。★从函数的来源

★从函数的形式●用户自定义函数:用于解决专门的问题。●无参函数:在调用时,主调函数和它之间无参数传递。●有参函数:在调用时,主调函数和它之间有参数传递。5/802.函数的定义★无参函数类型标识符函数名(){

声明部分

语句部分}●类型标识符指定函数值的类型,即函数带回来值的类型。

当函数的数据类型为int或无返回值时,可省略此项。★有参函数类型标识符函数名(形式参数表)形式参数说明{

声明部分

语句部分}6/80●函数形式参数说明的风格有两种。(1)func(inta,intb)(2)func(a,b)

inta,b;★例如:

[int]func(a,b)

inta,b;{

intc;c=a+b;

return(c);}函数头函数体参数说明函数说明执行语句定义语句执行语句★例如:

printstar(){printf(“***\n”);}无形参,无返回值7/803.函数的参数和函数的值★形式参数(形参):定义函数时函数名后括弧中的变量。★实际参数(实参):主调函数中调用一个函数时,函数名后括弧中的参数(可以是表达式)

。8/80★【注意事项】●实参与形参的类型应相同或赋值兼容。●在定义函数时指定的形参,并不占内存中的存储单元。只有发生函数调用时,形参才被分配内存单元。调用结束后,形参所占的内存单元也被释放。●实参可以是常量、变量或表达式,如:max(3,a+b);

但要求它们有确定的值。调用时将实参的值赋给形参。●在被定义的函数中,必须指定形参的类型。9/80●在C语言中,实参对形参的数据传递是“值传递”,即

单向传递,只由实参传给形参,而不能由形参传回来给实参。在内存中,实参与形参是不同的单元。执行被调用函数时,形参的值若发生改变,并不改变主调函数的实参的值。10/80★函数的返回值●格式:

return(表达式);“()”可省略●作用:把表达式的值返回给调用者;并中止被调函数的执行,把程序的控制权从被调函数返回到调用者。●通常,希望通过函数调用使主调函数能得到一个确定的值,这就是函数的返回值。11/80★【注意事项】●根据实际需要,函数中可以有return语句,可以没有

return语句,也可以有多个return语句。●函数的返回值应当属于某一个确定的类型,在定义函数时指定函数返回值的类型。

例如:int

max(floatx,floaty)/*整型

char

letter(charc1,charc2)/*字符型

double

min(intx,inty)/*双精度实型12/80●在定义函数时指定的函数类型一般应该和return语句中的表达式类型一致,不一致时以函数类型为准。●对于不带回值的函数,应用“void”定义函数为“无类型”(或称“空类型”)。这样系统就保证不使函数带回任何值,即禁止在调用函数中使用被调函数的返回值。此时在函数体中不得出现return语句。

13/80★例如:#include<stdio.h>voidmain(){inta,b,c;

scanf(“%d%d”,&a,&b);

c=max(a,b);/*调用函数max*/

printf(“maxis%d”,c);}int

max(int

x,inty)

/*定义函数max*/{intz;

z=x>y?x:y;

return(z);/*返回值z*/}

运行:7,8↙maxis814/804.函数的作用★例如:计算Cmn=m!/n!*(m-n)!。voidmain(){longt1,t2;inti,m,n;

printf(“Inputtwointeger(firstdata<=seconddata)”);

scanf(“%d%d”,&m,&n);

t1=1;

for(i=2;i<=n;i++)t1=t1*i;

t2=1;

for(i=2;i<=m-n;i++)t2=t2*i;

t1=t1*t2;

t2=1;

for(i=2;i<=m;i++)t2=t2*i;

t1=t2/t1;

printf(“Theresultis%ld”,t1);}/*n!*//*(m-n)!*//*m!*/3条for语句功能相同,都是求阶乘。15/80longjc(intn){longt=1;inti;

for(i=2;i<=n;i++)t=t*i;

return(t);}voidmain(){longt;intm,n;

printf(“Inputtwointeger(firstdata<=seconddata)”);

scanf(“%d%d”,&m,&n);

t=jc(n);

t=t*jc(m-n);

t=jc(m)/t;

printf(“Theresultis%ld”,t);}/*n!*/16/80●使程序的层次结构清晰,可读性更强、更好维护。★【函数的作用】●函数可以被不同的程序共享。●程序函数的引入有时还能大大提高程序的“编程效率”(注意不是运行效率)。●独立的函数更容易在程序设计中实现分工合作。由于函数的过程具有“隐蔽性”,其过程可“抽象成”函数的名称和参数接口,合作容易。17/803.2.2函数的执行★C语言将主调函数与被调函数平行、独立定义,且它们的相对位置没有限定。★当被调函数位于主调函数之后时,须在主调函数中声明要调用的函数

。★声明格式:函数类型函数名(参数表);【参数类型必须有,参数名可有可无】1.函数的声明18/80★注意:函数的“定义”和“声明”不是一回事。●函数的定义是对函数功能的确立,包括指定函数名,函数值类型、形参及其类型、函数体等,它是一个完整的、独立的函数单位。●函数声明的作用是把函数的名字、函数类型以及形参的类型、个数和顺序通知编译系统,以便调用该函数时系统按此进行对照检查。

19/80★例如:#include<stdio.h>voidmain(){

inta,b,c;

scanf(“%d%d”,&a,&b);

c=max(a,b);/*调用函数max*/

printf(“maxis%d”,c);}int

max(int

x,inty)

/*定义函数max*/{intz;

z=x>y?x:y;

return(z);

/*返回值z*/}

int

max(intx,inty);或int

max(int,int);

/*声明函数max*/20/80★格式:

函数名(实参表列);调用有参函数函数名();调用无参函数★函数调用时只需写出函数名称并在其后的括号中写出与形参个数相等的实参。★如果实参表列包含多个实参,则各参数间用逗号隔开。实参与形参的个数应相等,类型应匹配。实参与形参按顺序对应,一一传递数据。2.函数的调用21/80★函数调用的方式有3种。●函数语句:把函数调用作为一个语句,不要求返回值。例如:printstar();●函数表达式:函数出现在一个表达式中,要有返回值。例如:c=2*max(a,b);●函数参数:函数调用作为一个函数的实参。例如:m=max(a,max(b,c));

printf("%d",

max(a,b));22/80★C语言可以嵌套调用函数,即在调用一个函数的过程中,又调用另一个函数。

★C语言不能嵌套定义函数,即在定义一个函数时,其函数体内不能包含另一个函数。3.函数调用类型(1)函数的嵌套调用23/80★例如:

24/80★例如:main(){voidf1();voidf2();

printf(“gof1--\n”);f1();

printf(“ok!”);}voidf1(){printf(“inff--\n”);

printf(“gof2--\n”);f2();

printf(“returnmain--\n”);}voidf2(){printf(“inf2--\n”);

printf(“returnf1--\n”);}运行结果:gof1--inf1--gof2--inf2--returnf1–returnmain--ok!25/80★例如:

int

f(intx){……z=f(y);……}★在调用一个函数的过程中又出现直接或间接地调用该函数本身,称为函数的递归调用。(2)函数的递归调用26/80★例如:main(){

int

ff(int);

printf(“%d”,ff(3));}运行过程:(功能:求n!)int

ff(intn){if(n==1)return(1);elsereturn(n*ff(n-1));}ff(3)main3*ff(2)ff(3)2*ff(1)ff(2)return1ff(1)12*13*2输出627/80★【注意事项】●递归函数的执行分为两个阶段,第一阶段是向前调用阶段;第二阶段是向后返回阶段。●在递归调用函数中应该设置一个特殊条件,用以中止向前调用阶段,而进入向后返回阶段。28/80★函数间还可以循环调用。(3)间接递归调用★例如:函数A调用函数B,函数B再反回来调用函数A。29/80★例如:main(){int

f(int);

printf(“%d”,

f(3));}int

f(intn){int

g(int);if(n==1)return(1);elsereturn(n*g(n-1));}int

g(intn){if(n==1)return(1);elsereturn(n+f(n-1));}运行过程:f(3)main3*g(2)f(3)2+f(1)g(2)return1f(1)12+13*2输出630/80★函数间信息交互的方式有4种:★函数具有相对的独立性,但不是完全独立,主调函数和被调函数间必须有信息交互才能联合完成程序的总功能。4.函数间的信息交互●按名共享●被调函数返回主调函数信息●主调函数传递给被调函数信息●按地址共享31/80★按名共享:全局变量

●局部变量:在函数体中定义说明的变量,其作用域(使用范围)即定义它的函数体或语句体。●全局变量:在函数体外定义说明的变量,其作用域(使用范围)是在其后定义的所有函数。●不建议使用全局变量传递信息,因为它破坏了函数的独立性。●例如:int

x;main(){int

a=9,b=6;{int

t;t=a;a=b;b=t;}…}32/80★被调函数返回主调函数信息:被调函数名●通过return语句由被调函数返回信息给主调函数。●在主调函数中,被调函数名即能表示返回的函数值。●例如:printf(“%d”,f(3));33/80★主调函数传递信息给被调函数:参数值传递●被调函数的参数为形参;主调函数的参数为实参。●当形参为普通变量时,称为值参,它接受由实参传递来的数值(单向传递)。●例如:main(){inta;f(a);}

int

f(int

n){n++;…}●函数调用过程中形参的值发生改变,实参的值不变。34/80★按地址共享:参数地址传递●用全局变量传递信息的方式不被提倡。●普通变量作参数的值传递方式中,通过return语句被调函数只能返回给调用函数一个函数值。●为了能够方便、安全地从被调函数传送多个信息给主调函数,C语言提供了一种特殊形参—指针形参,其对应的实参是变量的地址。这样通过指针,在被调函数中就能引用主调函数中定义的变量,达到了函数间共享空间的功能,形式上实现了函数间双向传递信息的目的。35/80●例如:(“值传递”)voidswap(inta,intb){intt;t=a;a=b;b=t;}main(){inta=5,b=9;

swap(a,b);

printf(“a=%d,b=%d”,a,b);}运行结果:a=5,b=95a9b5tab595936/80●再例如:(“地址传递”)voidswap(int*pa,int*pb){intt;t=*pa;*pa=*pb;*pb=t;}main(){inta=5,b=9,*p_1,*p_2;

p_1=&a;

p_2=&b;

swap(p_1,p_2);

printf(“a=%d,b=%d”,a,b);}运行结果:a=9,b=5p_1&apa&ap_2&bpb&bab5t599537/80★用#include文件包含命令,通过链接C语言的头文件(后缀为.h的文件)来实现与标准库函数的链接。1.标准库函数的链接★一个头文件对应一类函数,而不是一个函数。3.2.3链接与建库★文件包含命令必须出现在文件的开头,且命令尾不要语句结束符“;”。★文件包含命令在编译之前由编译预处理系统执行。格式:#include“头文件名”38/80★系统默认头文件在“c:\tc\include”目录下。若TC系统安装在D盘根目录,则需在其集成环境下通过options

菜单下的directories项改变include目录和lib目录,分别改为:“d:\tc\include”和“d:\tc\lib”。★例如:#include“math.h”#include<math.h>★<>常用于标准文件,在系统设定的标准目录下搜索。★“”常用于自编文件,先在当前目录下查找,若找不到,再按系统设定的路径搜索。39/80★假设文件file.c中的函数要调用文件file1.c和file2.c中的函数,C语言提供以下几种方法进行文件间的链接。2.自编函数的链接●用#include文件包含命令●用TC集成环境中的“项目管理”功能●用tcc和link命令40/80★用#include文件包含命令●在调用函数所在文件file.c的开头,用#include文件包含命令指出子函数所在的文件路径。●格式:#include“盘符:路径\文件名”例如:#include“d:\tc\file1.c”#include“d:\tc\file2.c”41/80★用TC集成环境中的“项目管理”功能●在集成环境的编辑状态下,输入要链接的三个文件

(file.c,file1.c,file2.c)的路径,并保存成后缀为.prj(意为项目project文件)的文件,假设为file.prj。●在project菜单下选projectname项,并在对话框中输入file.prj,即可编译、链接、运行了。42/80★用tcc和link命令●将要链接的文件用编译命令tcc

进行编译成目标文件,后缀为.obj,然后用link命令将它们链接。●命令中不必写后缀,默认为.obj,执行完命令后生成的是可执行文件file.exe。方法:tccfile

tccfile1

tccfile2linkfile+file1+file2

43/80假设已编写好两个函数,voidf1(void)在文件f1.c中,

floatf2(inti,floatx)在文件f2.c中,把它们建成函数mycf,即可像标准库函数一样用#include“mycf.h”调用。3.建立自己的C语言函数库44/80●用命令tcc对f1.c及f2.c分别进行编译后产成两个目标文件f1.obj及f2.obj。

命令:tccf1.cf2.c★建立步骤:●将两个目标文件f1.obj及f2.obj建立成一个函数库文件

(取名为mylib.lib),并建立相应的例表文件(例表文件主要用来检查主函数库的使用情况)。

命令:tlib

mylib.lib+f1.obj+f2.obj,mylib.lft●将产生的mylib.lib函数库文件拷贝到标准库目录(即“c:\tc\lib”)下。45/80●建立头文件,可根据函数功能为文件起名,后缀为.h。设取名为mycf.h。

格式:externvoidf1(void);

externfloatf2(int,float);●将建立好的头文件mycf.h拷贝到目录c:\tc\include下。●在用户程序中,按下面的方式即可调用函数f1或f2。

#include<mycf.h>

main()

{…

f1();…f2();

…}46/803.2.4进一步认识变量、函数1.变量的空间属性——全局变量与局部变量★由于定义方式不同,其空间属性(作用域)就不同。●全局变量●局部变量

★变量的作用域又名变量的可见性。它是可引用某一变量的范围,只有在作用域内,程序才能使用它。★在C语言中,可按作用域将变量划分为两类:47/80~定义在函数体内(或复合语句体内),其作用域是本函数内(或本复合语句体内)。★局部变量例如:floatf1(inta)/*函数f1*/{int

b,c;…}charf2(intx,inty)/*函数f2*/{int

i,b;…}voidmain()/*主函数*/{int

m,n;…}

a、b、c有效x、y、i、b有效m、n有效48/80例如:voidmain(){intx;...{

inty;

...}...}

x的作用域y的作用域49/80★【注意事项】●主函数中定义的变量(m,n)也只在主函数中有效。主函数也不能使用其他函数中定义的变量。●在一个函数内部,不同复合语句(也称为“分程序”或“程序块”)中定义的局部变量也可以同名。●不同函数中可以使用相同名字的变量,它们代表不同的对象,互不干扰。例如:函数f1中的b和f2中的b。●形式参数也是局部变量。例如函数f1中的形参a,也只在函数f1中有效。50/80main(){intx=10;

printf(“\nx=%d”,x);{intx=99;x++;

printf(“\nx=%d”,x);}

printf(“\n-----”);{

intx=999;x++;

printf(“\nx=%d”,x);

}

printf(“\nx=%d”,x);.}

运行结果:x=10x=100-----x=1000x=10★例如:51/80~定义在函数体外,可以为本文件中其他函数所共用。它的作用域是从定义变量的位置开始到本源文件结束。★全局变量例如:intp=1,q=5;/*外部变量*/floatf1(inta){int

b,c;…}charc1,c2;/*外部变量*/

charf2(intx,inty){int

i,j;…}voidmain(){int

m,n;…}

p、q的作用域c1、c2的作用域52/80运行结果:x=11x=100x=1000x=100x=13★例如:intx=9;main(){intx=99;

func();x++;

printf(“\nx=%d”,x);{intx=999;x++;

printf(“\nx=%d”,x);}

printf(“\nx=%d”,x);

func();}func(){x+=2;

printf(“\nx=%d”,x);.}

●若全局变量与局部变量同名,则在局部变量的可见性范围内,同名的全局变量为不可见。53/80★【尽量不要使用全局变量】●全局变量在程序的全部执行过程中都占用存储单元,而不是在需要时才开辟单元。●全局变量降低了程序的可靠性和通用性。一般要求把函数做成一个封闭体,除了可以通过“实参——形参”的渠道与外界发生联系外,没有其他渠道。●使用全局变量过多,会降低程序的清晰性,人们往往难以清楚地判断出每个瞬时各个外部变量的值。在各函数执行时都可能改变外部变量的值,程序容易出错。因此,要限制使用全局变量。54/802.局部变量的时间属性—动态存储、静态存储★从变量值存在的时间(即生存期)角度来分,又可分为静态存储方式和动态存储方式。★静态存储方式(static):在程序开始执行时给变量分配存储区,到程序执行完毕才释放。

如:staticint

a,b;★动态存储方式(auto):在程序执行当中根据需要给变量分配存储区,并可能在程序没执行完前就释放。如:autoint

a,b;

55/80★例如:

f(intc){staticintb=9;b=b+c;

return(b);}main(){inta=9;

printf(“%d,”,f(a));

printf(“%d”,f(a));}运行结果:18,27说明:静态局部变量在编译时分配空间,且同时存储初值,只进行一次赋初值操作,因此这类变量在多次调用中具有“保值”功能。56/80★【注意事项】●变量的时间属性不改变变量的空间属性。●在函数中,inta;与autointa;完全等价。●定义静态局部变量时若不赋值,编译时自动赋值为0。f(intc){staticintb;b=c*c*c;}main(){inta=9;

f(a);

printf(“%d”,b);}无权引用b●静态局部变量具有“保值”功能,每一次相同的调用可能产生不同的结果,故请谨慎使用。57/80★按照内存的使用特性,可将其分为:●静态存储区●动态存储区●程序区(代码区)58/80★C语言中的每一个变量和函数有两个属性:●数据类型●数据的存储类型如:整型、实型、字符型……存储方式分为两大类:静态存储类和动态存储类。具体包含4种:自动的(auto),静态的(static),寄存器的(register),外部的(extern)。59/803.局部变量的存储方式★自动局部变量(自动变量)(auto)★静态局部变量(static)★寄存器变量(register)60/80★自动局部变量(自动变量)(auto)●函数中的局部变量,如不专门声明为static存储类别,都是动态地分配存储空间,数据存储在动态存储区。●例如:

int

f(inta){autointb,c=3;【auto可以省略】

…}●在调用该函数时系统为其分配存储空间并赋初值,每调用一次函数便重新给一次初值,在函数调用结束时自动释放存储空间。●如果在定义局部变量时不赋初值,则它的值是一个不确定的值。61/80★静态局部变量(static)●用static来声明,数据存储在静态存储区。●在整个程序运行期间都不释放所占用的存储单元。在编译时赋初值,在程序运行时它已有初值。以后每次调用函数时不再重新赋值,而只是保留上次函数调用结束时的值。●如果在定义局部变量时不赋初值,则编译时对其自动赋初值0(对数值型变量)或空字符(对字符变量)。●虽然静态局部变量在函数调用结束后仍存在,但其他函数不能引用它。62/80●例如:

#include<stdio.h>voidmain(){intf(int);

inta=2,i;for(i=0;i<3;i++)

printf(“f(a)=%d\n”,f(a));}

intf(inta){autointb=0;staticc=3;b=b+1;c=c+1;return(a+b+c);}运行结果:f(a)=7f(a)=8f(a)=963/80●

例如:输出1到3的阶乘值。

#include<stdio.h>voidmain(){int

fac(int

n);

int

i;

for(i=1;i<=3;i++)

printf(“%d!=%d\n”,i,fac(i));

}

int

fac(intn){staticintf=1;f=f*n;

return(f);}运行结果:1!=12!=23!=664/80★寄存器变量(register)●一般情况下,变量的值存放在内存中。当程序用到某一变量的值时,将内存中该变量的值送到运算器中。运算结束后,如果需要保存数,再从运算器将数据送到内存存放。

65/80●这种存放在CPU寄存器中的变量叫做寄存器变量,用关键字register来声明。●如果有一些变量使用频繁,则为存取变量的值要花费不少时间。为提高执行效率,C语言允许将局部变量的值放在CPU中的寄存器中,需要用时直接从寄存器取出参加运算,不必再到内存中去存取。●只有局部自动变量和形参可以作为寄存器变量,其它(如全局变量)不行。66/80●例如:#include<stdio.h>voidmain(){longfac(long);longi,n;

scanf("%ld",&n);

for(i=1;i<=n;i++)

printf("%ld!=%ld\n",i,fac(i));}longfac(longn){registerlongi,f=1;/*定义寄存器变量*/for(i=1;i<=n;i++)f=f*i;return(f);}运行结果:3回车1!=12!=23!=667/804.全局变量的存储方式★

全局变量定义在函数外部,可以为本文件中其他函数所共用。它的作用域是从定义该变量的位置开始到本程序文件结束。★

全局变量存储在静态存储区。★

全局变量的存储方式有:●外部全局变量(extern)●静态全局变量(static)68/80★静态全局变量(static)●静态全局变量在定义它的文件中有效,而不能被其他文件引用。能保证各文件的全局变量互不影响。例如:file1.c:file2.c:staticinta;static

inta;voidmain()voidfun(intn){{……}}69/80★外部变量(extern)●用extern来声明外部变量,以扩展外部变量的作用域。例如:

#include<stdio.h>voidmain(){int

max(int,int);

externa,b;/*外部变量声明*/

printf("%d\n",max(a,b));}

inta=13,b=-8;/*定义外部变量*/

int

max(int

x,inty){intz;z=x>y?x:y;

return(z);}70/80●用extern将外部变量的作用域扩展到其他文件。

例如:给定b的值,输入a和m,求a×b和am的值。文件file1.c:#include<stdio.h>#include“file2.c”inta;/*定义外部变量*/voidmain(){intpower(int);/*函数声明*/

intb=3,c,d,m;

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);}文件file2.c:externa;

/*声明已定义的外部变量a*/intpower(intn){inti,y=1;for(i=1;i<=n;i++)y*=a;

return(y);}71/80●

定义性声明(定义):需要建立存储空间。例如:extern

inta;

●引用性声明(声明)

:不需要建立存储空间。例如:externa;★变量的声明有两种。72/80(1)从作用域角度分,有局部变量和全局变量。★【存储类别小结】●它们采用的存储类别如下:局部

自动变量,

温馨提示

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

评论

0/150

提交评论