第七章 函数.docx
- 文档编号:12548714
- 上传时间:2023-06-06
- 格式:DOCX
- 页数:27
- 大小:262.78KB
第七章 函数.docx
《第七章 函数.docx》由会员分享,可在线阅读,更多相关《第七章 函数.docx(27页珍藏版)》请在冰点文库上搜索。
第七章函数
第七章函数
§7.1概述
结构化程序设计思想:
一个较大的问题应分解为若干个相对独立的子问题,每个子问题由一个程序模块实现(模块化)。
在C语言中,程序模块由函数来完成。
一个C程序可由一个主函数(main)和若干个函数构成。
由主函数调用其它函数,或其它函数间互相调用。
同一函数可以被一个或多个函数调用任意多次。
例:
main()
{printstar();
print_message();
printstar();
}
printstar()
{printf(“*******************************\n”);
}
print_message()
{printf(“Howdoyoudo!
\n”);
}
说明:
1.一个源程序文件由一个或多个函数组成。
一个源程序文件是一个编译单位,而不是以函数为单位进行编译。
2.一个C程序由一个或多个源程序文件组成。
一个源程序文件也可为多个C程序共用。
3.C程序的执行从main函数开始,调用其它函数后流程回到main函数,在main函数中结束整个程序的运行。
main函数是系统定义的。
4.函数不能嵌套定义,但可以互相调用,不能调用main函数。
5.函数的种类:
1)标准函数(库函数):
由系统提供。
2)用户自定义函数:
完成用户的任务。
6.函数的形式:
1)无参函数:
调用函数没有参数传送。
可以带回或不带回函数值(一般不带回)。
2)有参函数:
调用函数要进行参数的传递。
一般还要带回函数值。
§7.2函数定义的一般形式
一.无参函数的定义
类型标识符函数名()
{变量说明部分
执行语句部分
}
无参函数一般不需要带回函数值,因此可以不写类型标识符或用void表示。
二.有参函数的定义
类型标识符函数名(形式参数说明)
{变量说明部分
执行语句部分
}
如:
intmax(intx,inty)
{intz;
z=x>y?
x:
y;
return(z);
}
三.空函数
类型标识符函数名()
{}
§7.3函数参数和函数的值
一.形式参数和实在参数
形式参数:
在定义函数时,函数名后面括号中的变量名,简称“形参”;
实在参数:
在调用函数时,函数名后面括号中的变量名,简称“实参”。
例
main()
{inta,b,c;
scanf(“%d,%d”,&a,&b);
c=max(a,b);
printf(“\nMaxis%d”,c);
}
max(intx,inty)
{intz;
z=x>y?
x:
y;
return(z);
}
说明
1.在定义函数时,形参并不占用存储单元。
只有在调用函数时,才给形参分配内存,函数返回后,相应内存也释放。
2.实参为表达式(常量,变量),在调用时,将实参的值传送给形参(“值传递”)。
但若形参是数组,则传递的是数组的首地址(“地址传递”)。
3.在被定义的函数中,必须指定形参的类型。
4.实参和形参的类型必须一致(或赋值兼容)。
若类型不一致,则按赋值规则进行转换。
二.函数的返回值
1.函数的返回值是通过return语句获得的。
可以用如下格式:
return表达式或return(表达式)
2.如果不要返回值,可以不要return语句。
但还将返回一个不确定的值。
如下面语句是合法的(没有意义):
inta;
a=printstar();
为了明确表示“不带回值”,可以用“void”定义函数为“无类型”(或称“空类型”),这样将使上述语句非法。
3.一般应定义函数返回值的类型,若缺省类型说明,则表示返回值为整型。
4.一般函数返回值的类型应与return语句中的表达式类型一致。
若不一致,则以函数类型为准。
对数值型数据,可进行自动类型转换。
§7.4函数的调用
一.函数调用的一般形式
函数名(实参表列);
说明
1.若无参数,括号也不能省略;若有多个实参,用逗号隔开。
2.实参求值的顺序不确定,有的系统按从左到右,有的系统为从右到左。
二.函数调用的方式
1.函数语句:
printstar();
2.函数表达式:
c=2*max(a,b);
3.函数参数:
m=max(a,max(b,c));
或printf(“max=%d\n”,max(a,b));
三.一个函数调用另一个函数需具备的条件
1.被调用的函数必须存在(已定义);
2.若要调用标准函数(库函数),应在文件开头用#include命令将库函数包含到本文件中;
3.如果使用用户自己定义的函数,且与主调函数在同一个文件中,一般还应该在主调函数中对被调函数进行声明:
声明的一般形式:
类型标识符被调用函数的函数名(形参说明);
例
main()
{floatadd(floatx,floaty);/*floatadd(float,float);*/
floata,b,c;
scanf(“%f%f”,&a,&b);
c=add(a,b);
printf(“sumis%f\n”,c);
}
floatadd(floatx,floaty)
{floatz;
z=x+y;
return(z);
}
注意:
函数声明与函数定义的不同。
在以下情况下,可以省略对被调函数的声明:
1)若函数的返回值是整型或字符型。
(但没有作参数检查,因此不提倡)。
2)如果被调用函数的定义出现在主调函数之前。
例
floatadd(floatx,floaty);
floatadd1(floatx,floaty);
main()
{floata,b,c;
scanf(“%f%f”,&a,&b);
c=add(a,b);
printf(“%f,%f,%f\n”,a,b,c);
}
floatadd(floatx,floaty)
{
return(add1(x,y));
}
floatadd1(floatx,floaty)
{floatz;
z=x+y;
return(z);
}
§7.5函数的嵌套调用
C语言不能嵌套定义函数,但可以嵌套调用函数(在调用一个函数的过程中,又调用另一个函数)。
例:
弦截法求方程的根。
x3-5x2+16x-18=0
算法:
1.取两个不同点x1、x2,使得f(x1)、f(x2)的符号相反
(f(x1)*f(x2)<0)。
则(x1,x2)区间内必有一个根。
注意:
x1、x2的值不应差太大,否则在(x1,x2)区间内可能有多个根。
2.由f(x1)到f(x2)连一条直线,求该直线与x轴的交点x:
3.求f(x)的值,若|f(x)|<10-6(即f(x)≈0),则x为所求的值。
否则,若f(x1)与f(x)符号相反,从(x1,x)
若f(x)与f(x2)符号相反,从(x,x2)
重复2,3步骤。
用下面几个函数来实现各部分功能:
1)用函数f(x)来求x点的函数值;
2)用函数xpoint(x1,x2)求f(x1)到f(x2)连线与x轴的交点x的值;
3)用函数root(x1,x2)来求(x1,x2)区间的根。
#include“math.h”
floatf(floatx)
{floaty;
y=((x-5)*x+16)*x-18;
return(y);
}
floatxpoint(floatx1,floatx2)
{floaty;
y=(x1*f(x2)-x2*f(x1))/(f(x2)-f(x1));
return(y);
}
floatroot(floatx1,floatx2)
{floatx,y,y1;
do
{x=xpoint(x1,x2);
y=f(x);
y1=f(x1);
if(y*y1>0)x1=x;
elsex2=x;
}while(fabs(y)>=1e-6);
return(x);
}
main()
{floatx1,x2,f1,f2,x;
do
{printf(“inputx1,x2:
\n”);
scanf(“%f%f”,&x1,&x2);
f1=f(x1);
f2=f(x2);
}while(f1*f2>=0)
x=root(x1,x2);
printf(“\n方程的根是%f\n”,x);
}
例:
二分法求方程的根。
x=(x1+x2)/2
例:
牛顿迭代法(牛顿切线法)求下面方程在1.5附近的根。
2x3-4x2+3x-6=0
tg∝=f’(x0)=f(x0)/(x0-x1)
因此:
x1=x0-f(x0)/f’(x0)
而f’(x)=6x2-8x+3
按照以上公式循环迭代,直到|x1-x0|<10-5
#include“math.h”
main()
{floatx0,x1,f,f1;
x1=1.5;
do
{x0=x1;
f=((2*x0-4)*x0+3)*x0-6;
f1=(6*x0-8)*x0+3;
x=x0-f/f1;
}
while(fabs(x1-x0)>=1e-5);
printf(“Therootofequationis%5.2f\n”,x1);
}
§7.6函数的递归调用
函数的递归调用:
函数直接或间接地调用本身。
在递归函数中,要有条件结束递归调用,一般用if语句来控制。
例1用递归方法求n!
=1*2*3*…*n
floatf(intn)
{floatx;
if(n==0||n==1)x=1;
elsex=f(n-1)*n;
return(x);
}
main()
{intn;
printf(“Inputaintegernumber:
”);
scanf(“%d”,&n);
if(n<0)printf(“输入的数据出错。
\n”);
elseprintf(“%d!
=%f\n”,n,f(n));
}
若输入4,则递归调用过程如下:
例2猴子吃桃问题。
假设第一天的桃子数为f
(1),则:
intf(intn)
{intm;
if(n==10)m=1;
elsem=2*(f(n+1)+1);
return(m);
}
main()
{intm,n=1;
m=f(n);
printf(“桃子数=%d\n”,m);
}
例3Hanoi(汉诺)塔问题。
(只能用递归方法完成)
步骤:
函数hanoi(n,x1,x2,x3)表示将n个盘借助x2从x1搬到x3。
1.先把n-1个盘从x1搬到x2(借助x3)。
hanoi(n-1,x1,x3,x2)
2.把第n个盘从x1搬到x3。
3.再把n-1个盘从x2搬到x3(借助x1)。
hanoi(n-1,x2,x1,x3)
递归结束条件:
当n=1,则只执行第2步(只有一个盘)。
voidhanoi(intn,charx1,charx2,charx3)
{if(n==1)printf(“%c-->%c\n”,x1,x3);
else{hanoi(n-1,x1,x3,x2);
printf(“%c-->%c\n”,x1,x3);
hanoi(n-1,x2,x1,x3);
}
}
main()
{intn;
printf(“请输入盘子数=”);
scanf(“%d”,&n);
printf(“移动步骤如下:
\n”);
hanoi(n,’A’,’B’,’C’);
}
对于3个盘的情况如下:
n
1
x1
A
x2
B
x3
C
返
回
地
址
add1
main
§7.7数组作为函数参数
一.数组元素作为函数实参
数组元素作为函数实参,与一般变量做函数作参的用法是一样的,并且也是“值传递”,即“单向传递”。
例:
exch(intx,inty)
{intz;
z=x;
x=y;
y=z;
}
main()
{inta[5]={1,2,3,4,5},i;
exch(a[0],a[1]);
for(i=0;i<5;i++)
printf(“%d”,a[i]);
}
二.用数组名作函数的参数
例
exch(intx[5])
{intz;
z=x[0];
x[0]=x[1];
x[1]=z;
}
main()
{inta[5]={1,2,3,4,5},i;
exch(a);
for(i=0;i<5;i++)
printf(“%d”,a[i]);
}
说明:
1.用数组名作函数参数,则形参和实参必须是类型一致的数组。
2.形参和实参数组的大小可以一致,也可以不一致。
但若要求形参得到实参数组全部的元素,则形参数组与实参数组大小必须一致。
也可以不指定形参数组的大小(在定义形参数组时只用一个空的方括号),一般另设一个参数表示数组的大小。
3.数组名作函数参数时,不是“值传递”,而是把实参数组的起始地址传递给形参数组,这样两个数组就共用一段内存单元。
即“地址传递”。
特点:
形参数组中元素值的变化也将使实参数组元素同时发生变化。
三.用多维数组作函数参数
例:
有一个3*4的矩阵,求其中最大元素。
max(intarr[3][4])
{inti,j,max;
max=arr[0][0];
for(i=0;i<3;i++)
for(j=0;j<4;j++)
if(arr[i][j]>max)max=arr[i][j];
return(max);
}
main()
{inta[3][4]={{1,3,5,7},{2,4,6,8},{15,17,43,12}};
printf(“Themaxvalue=%d\n”,max(a));
}
说明:
1.在形参中定义多维数组时,也可以省略第一维的大小说明
如:
max(intarr[][4])
但不能用如下定义:
max(intarr[][])或max(intarr[3][])
2.实参为多维数组,形参可以是一维数组,反之亦然。
但要注意,形参数组的大小不能超过实参数组的大小(因超过的元素值是不确定的,但不会出现语法错误)。
§7.8局部变量和全局变量
一.局部变量
定义:
在一个函数内部定义的,只在本函数范围内有效的变量。
如:
floatf1(inta,intb)
{inti,j;
}
charf2(intx,inty)
{inti,j;
}
main()
{inti,j,m,n;
}
说明:
1.C语言中的函数都是相对独立、平等的(包括主函数),即主函数定义的变量不能被其它函数使用,主函数也不能使用其它函数定义的变量。
2.不同函数中可以定义相同的变量名,它们代表不同的内存单元,互不干扰。
3.形式参数也是局部变量。
4.在一个函数内部,可以在复合语句中定义只在本复合语句内有效的变量。
这种复合语句也称为“分程序”或“程序块”。
main()
{inta,b;
…
{intc;
c=a+b;
…
}
…
}
二.全局变量
定义:
在函数之外定义的变量为外部变量,称为全局变量。
它可以被多个函数使用。
全局变量的有效范围:
从定义变量的位置开始到本源文件结束。
intp=1,q=5;
floatf1(inta)
{inti,j;
…
}
charc1,c2;
charf2(intx,inty)
{inti,j;
…
}
main()
{inti,j,m,n;
…
}
说明
1.全局变量可被多个函数使用,因此一般可用于函数之间的联系(另一种联系方法是通过参数和函数返回值)。
但使用全局变量将降低程序的清晰性和通用性。
一般全局变量的第一个字母用大写表示。
2.如果在定义点之前的函数想引用该外部变量,则应在该函数中用“extern”作“外部变量声名。
3.如果在同一个源文件中,外部变量与局部变量同名,则在局部变量的作用范围内,外部变量不起作用。
§7.9动态存储变量与静态存储变量
一.变量的存储类别
从变量的作用域(作用范围)来划分:
全局变量(外部变量)和局部变量(内部变量)。
从变量的存在时间(生存期)来划分:
静态存储变量和动态存储变量。
分配给用户使用的内存空间分为三部分:
程序区
静态存储区
动态存储区
其中:
在静态存储区中的变量为:
全局变量和静态变量。
特点:
在程序开始执行时分配存储单元,程序执行完毕后释放。
在动态存储区中的变量为:
局部自动变量,形参,函数调用时的现场保护和返回地址。
特点:
在函数调用开始时分配存储单元,函数结束时释放。
在C语言中每一个变量和函数有两个属性:
数据类型和数据存储类别。
存储类别:
静态存储类别和动态存储类别。
具体的有四种:
自动(auto),静态(static),寄存器(register),外部的(extern)
二.局部变量的存储方式
1).自动变量:
用auto作存储类别说明或不作任何存储类别说明(缺省)。
如:
intfunc(inta)
{autointx,y=3;
floatz;
…
}
2).静态局部变量:
用static作存储类别说明。
如:
func(inta)
程序区
c
静态区
a
main
动态区
i
a
func
b
{intb=0;
staticintc=3;
b=b+1;
c=c+1;
return(a+b+c);
}
main()
{inta=2,i;
for(i=0;i<3;i++)
printf(“%d”,func(a));
}
说明:
1.局部静态变量在静态存储区分配单元,在编译时赋初值(只赋一次初值)。
因为在程序运行过程中存储单元不释放,因此每次函数调用时使用了上次函数调用结束时保留的值。
2.自动变量在动态存储区分配单元,在函数调用时动态分配存储单元并赋初值,函数结束时释放。
3.静态局部变量若不赋初值,则在编译时系统自动赋初值0(数值型)或空字符(字符型)。
自动变量若不赋初值,则其值不确定。
4.虽然局部静态变量在函数调用结束后仍然存在,但其它函数是不能引用它的。
3).寄存器变量:
用register作存储类别说明。
一般把循环控制变量定义为register。
如:
registerinti,j;
说明:
1.只有自动变量和形参可以定义为register。
2.在MSC、TurboC等微机上用的C语言,对register当作自动变量处理。
三.全局变量的存储方式
全局变量在编译时分配在静态存储区。
若C程序由几个源程序组成,则在一个文件中定义的全局变量,允许其它文件中的函数引用,只要在引用的文件中用extern说明。
例:
文件file1.c
inta;
main()
{intpower(intn);
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
externinta;
power(intn)
{inti,y=1;
for(i=1;i<=n;i++)
y*=a;
return(y);
}
注意:
在file2.c中的extern说明不是在函数的内部(在函数内用extern说明使用的是本文件的全局变量)。
它首先检查变量是否在本文件的后面定义,若不是则检查是否在与之连接的其它文件中定义,若不是则出错。
静态外部变量:
只能被本文件中的函数引用。
如:
staticinta;
main()
{…}
四.存储类别小结
§7.10内部函数和外部函数
一.内部函数(静态函数)
定义:
只能被本文件中其它函数调用的函数。
定义的一般形式:
static数据类型标识符函数名(形参表)
如:
staticintfun(inta,floatb)
说明:
使用内部函数,可以使函数只局限于所在文件,如果在不同的文件中有同名的内部函数,互不干扰。
二.外部函数
定义:
可以被其它文件中的函数调用的函数。
定义的一般形式:
[extern]数据类型标识符函数名(形参表)
我们以前定义的函数都是外部函数。
说明:
在调用外部函数的文件中,一般要用extern声明所调用的函数是外部函数(但可以省略extern)。
在有些系统中也可以不作声明。
由多个文件组成的C语言程序,编译过程有两种方法:
1.先分别对每个文件进行编译,得到多个.obj文件。
然后用link把这些目标文件(.obj)连接起来。
2.在一个文件中用#include命令将其它文件都包含到这个文件中,使之成为一个文件。
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 第七章 函数 第七