c语言基础教程第五章函数和存储类免费版.docx
- 文档编号:12328325
- 上传时间:2023-04-18
- 格式:DOCX
- 页数:37
- 大小:93.10KB
c语言基础教程第五章函数和存储类免费版.docx
《c语言基础教程第五章函数和存储类免费版.docx》由会员分享,可在线阅读,更多相关《c语言基础教程第五章函数和存储类免费版.docx(37页珍藏版)》请在冰豆网上搜索。
c语言基础教程第五章函数和存储类免费版
完全免费版
第五章函数和存储类
5.1.1函数的定义
如果想调用一个函数完成某种功能,必须先按其功能来定义该函数。
函数定义的格式如下所示:
<存储类说明><数据类型说明><函数名>(<参数表>)
<参数说明>
{
<函数体>
}
函数的定义可分两大部分:
函数头和函数体。
函数头包含(函数名>和(参数表)以及关于该函数的(存储类说明)、《数据类型说明),还有<参数说明).函数体是由一对花括号括起来的若干语句组成的,函数体内可以有一条语句或多条语句,也可以有复合语句,还可以是空,即该函数什么操作也不做,这是一个最简单的函数。
(函数名)的起法同标识符,能够通过函数名来标明该函数的主要功能为最好。
(函数名>后面跟一对圆括号,内有一个<参数表、,该'参数表)由一个或多个参数构成,多个参数之间用逗号分隔,也可以没有参数,但圆括号不可省略。
定义瞬数时要对该函数的存储类和数据类型进行说明。
函数的存储类有两种:
外部函数和内部函数。
外部函数用extern关键字加在函数名前面进行说明,常常省略,凡是不加存储类说明的函数都是外部函数。
内部函数必须在函数名前面加stai}c关健字进行说明,内部函数又称静态函数。
函数的数据类型是该函数返回值的类型,函数的数据类型千一分丰富,一般C语言所允许的数据类型大多都可作为函数类型,个别的除外,如联合类型等。
C语言规定,数据类型中除了
int型的可以不要说明外,其余各种类型都需说明。
如果一个函数具有参数时,则需对参数的类型进行说明。
<参数说明)一般放在函数名的下一行,有的编译系统(如TurboG)可以把参数说明放在参数表中。
下面是函数定义的几个例子:
Nothing()
{ }
该函数名字是nothing,它没有参数,它的函数体是空,该函数什么也不做。
空函数可以用于调试中,它表明应该调用一个函数,但该函数尚未编好,先用一个空函数顶替。
voidnopa()
{
printf("oK!
\n");
}
该函数名是nops,该函数没有参数,因此不必进行参数说明,这里,void关键字用来说明该函数没有返回值。
该函数的函数体内仅有一个语句。
floatmax(x,y)
floatx,y;
{
floatz
z一x>y?
x:
y:
return(z);
该函数名是max,它有两个参数x,y,它们都是float型数,由于该函数有参数,因此需要
参数说明:
floatx,y;该函数的数据类型为float型,这说明该函数有返回值,而返回值的类型是float型。
该函数。
无存储类的说明,这意味着该函数的存储类为外部的。
该函数体是由3条语句组成的。
这里有说明语句,它被放在执行语句的前面。
在函数的定义中还应注意如下几个问题:
(1)C语言中的函数可分为有返回值和无返回值两大类。
在有返回值的函数定义中,除int型返回值外都必须对返回值的类型进行说明。
在无返回值的函数定义中,可以加上无返回值的说明符void,也可以不加。
这样,对于一个不加数据类型说明的函数可能是无返回值的,也可能是有int型返回值的。
(2)函数的定义不能嵌套。
这就是说,不能在一个函数的函数体内再定义一个函数。
例如,下列写法是错误的:
fl(x,y)
intx,y;
(
f2(a,b)
inta,b;
{
Return(a十b);
}
这种企图在f1()函数中再定义一个f2()函数的做法是错误的。
但是,函数的调用允许嵌套。
这就是说,可以在一个函数体内调用另一个函数,也可以进行自身调用(又称递归调用).
(3)如果一个函数的函数体中有变量的定义或说明时,一定要放在执行语句的前边,不可放在中间或后面,否则要出编泽错。
5.1.2函数的说明
函数定义好后,在调用之前一般地要进行说明。
函数的说明方法有如下两种:
一是只说明函数的类型,这称为简单说明。
例如,对前面定义过的函数max说明如下:
floatmax();
这种说明可以单独一行,也可以与其他同类型的变量放在一起。
二是不仅说明函数的类型还要说明其参数的类型,这称为原型说明。
例如,对前面己定义过的函数max()原型说明如一F:
Boatmax(float,float);
原型说明要比简单说明复杂一些,但是用原型说明后,在函数调用时系统将其参数的类型进行检查,如果发现不一致,则报错。
如果用简单说明方法,则在调用时不作参数类型是否一致的检查,即使不一致也不报错,由此可见,原型说明比简单说明要安全些。
在实际使用中关于函数的说明还需注意如下几点:
(1)在下列情况下,函数在调用之前不必说明:
在定义函数时没有加任何说明;或者是该函数无返回值义没有加void说明符;或者是该函数具有int型返回值而省略说明符。
(2)助在下列情况下,函数在调用之前必须说明,如不说明,将出现编译错:
即在定义函数时加了说明符,包含void在内,又是先调用后定义(即调用在定义之前),则调用之前必须说明。
(3)在下列情况下,即在定义函数时加了说明符,包含void在内。
而先定义后调用(即定义在调用之前),则调用函数之前可以说明也可以不说明。
有时为了使得系统能够检查函数调用时其参数类型是否一致,对于不必须说明函数的情况也对函数进、
5.2.1函数的参数
C语言中,有的函数有参数,可以一个或多个,多个参数用逗号分隔,而有的函数没有参数,但是函数名后面的圆括号不可省略,例如,前面讲过的主函数main()就是属于没有参数的函数。
但是,主函数也可以有参数,后面会讲到,
函数参数分为形式参数(简称形参)和实在参数(简称实参)两种。
形参是指在定义函数时,(参数表)中的参数,因为该参数在该函数被调用之前是没有确定值的,只是形式仁的参数,只有被调用时通过实参来获取值。
实参是指调用函数的参数,因为该参数已具有确定的值,是实在的参数:
实参可以是表达式,在调用之前先计算出表达式的值。
在实际应用中关于形参和实参的使用还应注意如下几点:
(1)在定义函数时,所指定的形参在该函数被调用之前是不被分配内存单元的。
只有在发生函数调用时,才给形参分配单元,井且赋值,一旦函数调用结束后,形参所占的内存单元又被释放掉。
因此,形参属于局部变量,其作用范围仅在定义它的}J数体内。
(2)函数调用时所用的实参是个具有确定值的表达式,调用时先计算表达式的值,再将其值传递给对应的形参。
(3)函数的形参是属于定义它的函数的局部变量。
因此,允许一个函数的形参和实参同名,因为它们在内存中占有不同的存储单元。
(4)函数调用要求形参和实参在个数上相等,并且对应的参数类型相同。
否则将会发生"个数不匹配"或"类型不一致"的错误。
一般情况下,即使不报错误信息,也会造成结果不正确。
下面举一个简单的函数调用的例子,通过它进一步说明函数的定义格式,函数的说明方法和形参与实参的不同。
[例5.1]求两个float型数的和。
程序内容如下:
main()
{
floata,b,sum;
floatfadd();
a=5.6;
b=7,2;
sum=fadd(a,b);
printf("%.2f\n”,sum);
}
floatfadd(x,y)
floatx,y
{
retutrt(x+y);
执行该程序输出如下结果:
12.80
说明:
(1)该程序中定义了两个函数:
main()和fadd().前一个是主函数,它无参数,也无返回值。
后一个是具有float型返回值的带有参数的函数,该函数有2个参数x和Y,它们都是float型变量。
(2)在主函数main)中,floatfadd();这是对函数fadd()的简单说明,只说明函数的类型是float型(即返回值的类型).如果甩原型说明应采用如下格式:
floatfadd(#loat,float);
这种情况下,对fadd<)函数必须说明,因为该函数返回值非int型的,并且又是调用在先定义在后。
如不说明、则会出现编译错,读者可以上机验证。
(3)该程序中,x和Y是fadd<)函数的两个形参,关于形参类型说明的方法,除了本程序中说明方法外,还可以采用如下方法:
floatfadd 即在参数表中就说明了参数的类型。 但是,这种说明方法并不是所有编译系统都允许。 而TuibaC编译系统是允许的。 该程序中,a和b是fadd()函数的两个实参,在调用该函数时,a和b的值是确定的。 该程序中的函数调用时满足了形参与实参个数相等,对应类型相同的要求。 5.2.2函数的返回值、 C语言中,有的函数带有返回值,有的函数不带有返回值。 函数的返回值都是通过return(表达式))语句来实现的。 例如,在本章例5.1中,fadd()函数的函数体中的return(x+y)十妇;语句就是将表达式x十Y的值作为返回值,返回给调用函数fadd()的值赋给变量sumo返回值的类型是该函数的类型。 带有返回值的return语句具体实现过程如下: (1)先计算return语句中《表达式)的值, (2)根据函数的类型对哎表达式)的类型进行转换,使得(表达式》的类型转换成为函数的类型; (3)将<表达式>的值和类型返回给调用函数,作为调用函数的值和类型。 一般情况下,要设置一个变量来接收这一返回值。 (4)将程序流的控制权交给调用函数,继续执行调用函数下边的语句。 在不带返回值的,turn语句中。 不执行上述 (1)至(3)步较,只执行(4)步魏。 函数的返回值(简称函数值)的类型是在定义函数时指出的一般情况下,除了返回值为int型的不要说明函数的类型外,对非int型返回值的函数都应指出西数值的类璧。 函数遭的类型和、turn语句中表达式的类型可能不一致,这时藉要转换,即将表达式的类型转换成为函数值的类型,这种转换是自动进行的。 下面通过一个实例说明当函数值的类型与return语句表达式的类型不一致的情况时是如何自动转换的。 [例5.2求]两个输入的浮点数的和,最后的和用int型表示。 程序内容如下: main() { foatx,y, intsum; printf("Inputx,y: “); scant("%f%f,\&x&xy"); sum=add(x,y); printl("sum=%d\n",sum); } addt(x,y) floatx,y. { return(x+y); 运行情况如下: Inputx,y: 1,3,7,9 sum=9 说明: (1)该程序由两个函数组成,一个是main()函数,它无参数,也无返回值;另一个是add()函数,它有两个参数x和Y}都是float型的。 它有int型返回值。 因此,定义该函数时可以不加类型说明,调用该函数之前也可不加说明。 (2)在main()中,x和Y作为addt)函数的实参;在add()函数中,x和Y作为add函数的形参。 虽然名字相同,但它们在内存中有着不同的存储单元。 调用add()函数时,其形参与实参是一致的,即个数相等,对应类型相伺。 (3)在被调用函数add()中,返回值的表达式x十Y的类型是float型的,其值应为i_.3十7.9,即为9.20由于函数add必的类型为int型的,因此,要将9.2自动转换为int型数9,再返回给调用函数,即赋值给int型变量sum。 5.3.1传值谓用的特点、 这里所讨论的传值调用是指传递变量值的调用方式。 在这种调用方式中,实参使用变量名或者表达式,形参使用变量名。 在调用时,调用函数将实参值拷贝到一个副本给形参,即使形参按顺序从对应的实参中获得值,这就相当于将实参值对应地赋给形参,使形参获值。 这种调用方式具有如下特点: 被调用函数的参数值被改变而不会影响调用函数的参数值,因此安全性好。 [例5.3] 分析下列程序输出结果。 执行该程序输出结果如下: Main() ( floata,b; voidfl()" a=7.2; b=3.6; fl(a*b); printf('"%,2f\n",a,b) } voidfl(x*y) floatx.y; ( x+=0.5; y一=0.5; printf("%.2f,%.2f\n",x,y); { 7.70,3.10 7.20,3.60 说明: (1)该程序由两个函数组成,一个是主函数main(),另一个是被main()函数调用的被调用函数f1().在定义fl()函数时,使用了void说明该函数无返回值。 因此,在调用f1()函数之前必须说明flt)函数,因为调用在先定义在后。 (2)主函数中,调用fl()函数时,两个实参都是变量,即a和b.被调用函数的两个形参也都是变量,即x和y,这是属传递变量值的传值调用方式。 在被调用函数fl()中,通过两个赋值表达式语句分别对形参x和Y的值进行改变,并通过printf()语句输出改变后的x和Y的值: 7.70,3,10.返回主函数后,执行printf)语句输出主函数中的a和b值为7.20,3.60.可见,a和b值没有因为调用函数fl()而改变其值。 尽管在fl()函数中对其形参x和Y进行了改变。 可见,这种传值调用方式使得被调用函数中参数值的改变不影响调用函数的参数值,其原因是因为这种调用方式是调用函数将其实参值拷贝了一个副本给了被调用函数的形参,被调用函数中改变形参的值,只是改变了副本中的值,对调用函数中的"正本"没有改变。 5.3.2传址调用的特点 传址调用是指在调用时传递变量地址值的传值调用。 传址调用时要求调用函数的实参用地址值,而被调用函数的形参用指针,于是函数之间进行地址值的传递。 这种传递是将实参的变量地址值传递给形参指针,即让形参指针指向实参变量,这种传递方式与调用函数拷贝实参值的副本给形参是不同的,它是让形参指针直接指向实参的变量。 这种传址调用具有如下特点: 被调用函数中可以通过改变形参所指向的内容来改变调用函数的实参值: 这一特点与前面讲过的传值调用是截然不同的。 传址调用可以通过改变形参所指向的内容来改变实参值,这就提供了函数之间进行信息传递的又一渠道,并且这种传递信息的方式还可以克服前面讲过的返回值方式的只传一个信息的局限性。 因此,传址调用在C语言函数调用中是经常采用的方式。 后面还会看到这种调月方式将会带来其他好处。 下面举一个传值调用方式和传址调用方式不同的例子,并且将看到这两种传递方式可在同一个k}数调用中实现。 [例5.4] 分析下列程序输出结果。 Main() { Int a,b,c, b=c=a=5; Fl(n,&b,&c); printf(f'%d,%d,''d\n",a,b); ) Fl(x,y.z) intx*y,*z: { X*-2 y一一x; *z=x十*y: printf("%d,%d,%d\n",x*Y,*z) 执行该程序输出如下结果: 10,15,25 5,15,25 说明: (1)该程序由主函数main()和被调用函数fl()组成,fl()函数无返回值,由于定义fl()时没有加任何类型说明符,尽管调用在先定义在后也不必说明。 (2)在调用函数fl()时,3个实参中,一个是用变量名,另外2个用变量的地址值;在对应的形参中,一个是用变量名,另外2个用指针。 在调用fl()函数中,有一个参数属于传值调用,2个参数属于传址调用。 在被调用函数f1()中,改变了变量x的值,由于传值调用对调用函数对应实参a的值并没有被改变,这是传值调用的特点。 同时,通过用运算符,对指针y和: 取其内容,并且通过赋值表达式语句来改变了x和Y的内容,于是调用函数中对应的实参b和c的值被相应改变,这便是传址调用的特点。 该例程序中值得注意的是要在传址调用中通过被调用函数来改变调用函数的参数值时,一定要改变形参指针所指向的内容,而不能只改变形参的地址值,改变形参地址值是不会影响对应实参的变量值的。 下面的例子将说明这一点。 [例5.5] 分析下列程序的输出结果。 main() { inta,b,c,d; a=b==c=d=5; f1(&a,&b); f2(&c,&d), printf("%d,%d,%d,%d\n,a,b,c,d); } fl(x,y) int*x,*Y} { *x*=2; y+=*x; printf("%d,%d\n,*x,*y); } f2(m,n) int*p,*q; p=q=8; p=q=8; m=&p n=&q printf("%d,%d\n",*m,*n); } 执行该程序输出结果如下: 10,15 8,8 to,15,5,5 说明: (1)该程序由三个函数组成,除了主函数main()外,还有二个被调用用函数fl()和f2()。 这两个被调用函数都是没有返回值的。 在main()函数中,先调用f1()函数,再调用f2()函数。 (2)在调用fl()函数时采用了传址调用方式,即实参用变量a和b的地址值&a和&b,形参用指针x和Y.在fl()函数中,通过使用取内容运算符改变了指针x和Y所指向的变量的值(即改变了指针所指向的内容).因此,fl()函数被调用后,土函数中变量a和b的值发生了变化,这是通过传址调用改变调用函数参数值的又一个例子。 (3)在调用f2()函数时也采用了传址调用方式,即实参用变量。 和d的地址值&c和&d,形参用指针m和n.在f2()函数中,形参m和n的地址值发生了变化,使m和n指向了变量P和q,于是m和n所指向的内容将是P和9变量的值8.这时,如果要再改变m和n所指向的内容,只是改变变量P和q的值,也不会影响调用函数的参数值。 因此,简单地说"传址调用中通过改变形参的值来改变调用函数中参数的值"这句话是不确切的。 一定要指出在被调用函数中通过改变形参所指的内容才能改变调用函数中实参的值。 5.3.3数组名作参数的函数调用 数组元素可作为实参,实现传值调用,这与变量名作实参一样,都是单向传递的。 数组名作函数实参与数组元素作实参是不同的,因为C语言规定数组名是一个地址值,即是该数组首元素的地址值。 因此,数组名作实参时,要求形参也是数组名或者指向数组的指针,关于指向数组的指针将在下一章"指针"中讲解,这时实现的不是传值调用,而是传址调用。 调用函数不是将整个数组的所有元素拷贝成副本传递给被调用函数,而是只将其数组的首元素地址值传给形参数组,于是这两个数组将共同占用同一段内存单元,这就是让形参数组的首元素地址与实参的首元素地址相同,使得这两个数组的对应元素同占一个内存单元。 因此要求这两个数组要类型相同,数组的大小可以一致,也可以不一致。 如果要使形参数组得到实参数组的全部元素,则形参数组与实参数组应大小一致。 下面举例说明用数组名作函数参数的调用方法。 [例5.6]编程对某一数组中的各个int型数进行由小到大的排序。 本例中采用算法简单的选择排序法。 程序内容如下: main() { voidsort(); staticinta[8]={5,一2.9,87,0,6,21.49: }; Intin: Sort(a.n); for(i=O;i<8;i十十) printf("%4d",a[i]); printf("\n"); } voidsort(b,m) intb[],m; { inti,j,k,temp; for(i=O;i { k=i; for(j=i+1;j if(b[j] k=j; temp=b[]; B[k]=b[i]; b[i]=temp; } } 执行该程序输出结果如下: 一2 0 5 6 9 21 49 87 说明: (l)该程序是通过调用sort()函数对已知数组a中的8个int型数进行由小到大排序。 被调用函数sort()是没有返回值的。 (2)主函数中调用函数格式如下: sort(a,n) 其中,实参a是一个已知的数组名,该数组是一个一维数组,8个int型元素。 实参n的值为8.可见,sort()函数的两个实参中,一个是地址值,另一个变量值。 被调用函数sort()的两个形参中,一个是数组名b,它是一个没有指定大小的一维int型数组,其类型与数组a相同,其大小将与所对应的实参数组大小相等。 另一个形参是变量m.因此,不难看出该函数调用中,第一个参数是属于传址调用,第二个参数是传值调用。 在传址调用中,将数组a的首元素地址传给b数组,使得a,b两个数组共占同一段内存单元。 由于是传址调用不必拷贝数组元素的副本,因此,效率较高,节省了时间和空间的开销。 这是传址调用的又一好处。 由于a,b数组共占同一段内存单元,因此在。 rt()函数中,通过b数组元素的改变,而使得数组a的元素发生了变化,于是完成由小到大的排序。 (3)在函数sort()中,选择排序法的算法是这样的: 通过双重for循环,每作一次外重for循环,从指定的数中挑出最小的一个放在指定的元素位置,例如,第一次外重for循环,从5个数中挑出最小的放在第。 个元素的位置,第二次外重循环从剩下的7个数中再挑出最小的放在第1个元素的位置,依次类推,外重for循环共进行7次,便将8个数的顺序由小到大排好。 内重for环是用来确定在要查找的数中找出最小数所对应的下标值,并将它赋给k变量。 然后,通过三个赋值语句将数值最小的那个元素换到待选数中的最前边。 即下标值为i的位置。 这种排序方法每次找出一个最小的数放在前边,直到最后剩下一个数为止。 5.3.4函数的嵌套调用、 所谓函数的嵌套调用是指在调用一个函数的过程中,又调用另外一个函数。 例如,在调用A函数的过程中,还可以调用B函数,在调用B函数的过程中,还可以调用C函数,…当C函数调用结束后返回到B函数,当B函
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 语言 基础教程 第五 函数 存储 免费版