版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第七章函数目录CONTENTS函数的概念与分类7.1函数的定义、参数与返回值7.2函数的调用7.3函数的嵌套调用和递归调用7.4数组作函数参数7.5变量的作用域与存储类别7.6编译预处理7.7智能控制项目实践附
函数的概念与分类7.1函数的概念与分类函数的概念函数的分类函数的概念01
将一个C程序分为若干模块,每个模块实现一个特定的功能,在C语言中用函数来实现模块的功能。
函数是一个独立的具有特定功能的程序模块。#include<stdio.h>intmin(intx,inty){
if(x>y)
returny;else
returnx;}intmain(){
intmin(intx,inty);/*函数声明*/inta,b,c;printf("inputtwonumbers:\n");scanf("%d%d",&a,&b);
c=min(a,b);/*函数调用*/printf("minmum=%d",c);
return0;}引例:从键盘输入两个整数,输出其较小的数。函数的分类02函数定义角度用户自定义函数库函数函数功能角度无返回值函数有返回值函数数据传送角度有参函数无参函数1.从函数定义角度:①标准函数(库函数);②用户自己定义的函数库函数:是由编译系统提供的已设计好的函数,用户只需调用而无需要去实现它。前几章用过的scanf,printf,getchar,
putchar等都是库函数。用户自定义函数:由程序员自己定义和设计的函数。需要程序员自己来编写函数功能实现代码。函数的分类022.从函数功能角度看:①有返回值函数;②无返回值函数
int
max(int
x,
int
y
){……
return
z;}intmain()
{……
c=max(a,
b
);}intchange(intx,inty){intt;t=x;x=y;y=t;}函数的分类023.从数据传送角度:①无参函数;②有参函数无参函数例子:main(){print_star();/*调用print_star函数画****/print_message();/*调用print_message函数写字*/print_star();/*调用print_star函数画****/}intprint_star()/*定义print_star函数*/{printf(“\n**********”);}intprint_message()/*定义print_message函数*/{printf(“\nHello!”);}函数的分类02②有参函数有参函数例子:(输出两数中大者)#include<stdio.h>intmax(intx,inty);main(){intnum1,num2,a;scanf("%d,%d",&num1,&num2);a=max(num1,num2);printf("max=%d",a);}intmax(intx,inty){intz;if(x>y)z=x;elsez=y;returnz;}函数的分类02函数的定义与函数的返回值7.2
函数的定义与函数的返回值函数的定义函数的参数和返回值函数的定义011、无参函数的定义定义格式:类型说明符函数名(){说明部分语句}intprint_message(){printf(“\nHello!”);}函数头函数体类型标识符
函数名()该函数值的类型,即函数返回值的数据类型
1)函数类型是指函数值的数据类型
如:
int
max(x,y)
返回值为整型
char
letter(c1,
c2)
字符型
double
min(x,y)
双精度型
函数的定义01类型标识符
函数名()
void
time(longa)
/*无返回值*/
{inti;for(i=1;i<=a;i++);
}/*延迟一个小的时间片*/
void
main(){time(10000
);}void─
表示函数不返回任何值称“无类型”或“空类型”函数的定义01如果省略函数类型,则默认为
int
型。如:
max(int
x,
int
y)类型标识符
函数名()
int
max(int
x,
int
y)函数的定义01类型标识符函数名()2)函数名:
可以是任何有效的标识符。在一个程序中除了主函数外其余函数的名字可以任意取,但应有意义。“()”函数标志intmax(intx,inty){if(x>y)returnx;elsereturny;}函数的定义01
函数体用来完成具体任务的一个程序段。
数据说明
执行语句
intmax(intx,inty){intz;
/*本函数体内所用变量的说明*/z=x>y?x:y;return(z);}函数体内若无任何语句时,为空函数。结构:dump(){
}不能在函数体内定义形参。函数的定义01注意:在函数体内不能定义函数,即函数不允许嵌套定义。如:
intmain(){int
fun()
{…}}×函数的定义01函数的定义01例7.1:调用无参函数输出菜单。 //*******无参函数的定义与调用****** #include<stdio.h> voidmenu() { printf("***欢迎光临重庆老火锅***\n"); printf("鲜毛肚20元/份\n"); printf("鲜鸭肠18元/份\n"); printf("嫩牛肉18元/份\n"); printf("海带苗6元/份\n"); } voidmain() { menu(); } 二、有参函数的定义形式类型标识符函数名(形式参数表列){
说明部分
语句}形式参数(简称形参)可以是各种类型的变量,各参数之间用逗号间隔,必须在形式参数表中给出形参的类型说明。在函数调用时,主调函数将赋予这些形式参数以实际值。
intmax(int
x,inty){if(x>y)returnx;
elsereturny;
}函数的定义01类型标识符函数名(形式参数表列)形式参数可以是变量名、数组名,不允许是常量、表达式或数组元素。定义函数时参数表中的参数称为形式参数,用逗号分隔。
如:intmax(intx,inty)
如:floatfact(n,x[i])×形参省略时称无参函数,但此时函数名后的圆括号不能省。dump(){
}函数的定义01类型标识符函数名(形式参数表列)参数说明指明形参的类型
在定义函数的时候,必须为每个参数指定数据类型,但int可以省略。说明方法有两种:如:按传统方式说明形参intmax(x,y)
intx,y;
{
……
}按现代方式说明形参max(intx,inty){
……
}这两种方式都可以使用,但推荐使用现代方式注意这里是逗号函数的定义01常见的程序设计错误有:把同一种类型的参数声明写成:
floatmax(floatx,y);
正确的是:floatmax(floatx,
floaty
)或:floatmax(x,
y
)
floatx,
y
;在定义函数时,在右圆括号后使用分号。如:floatmax(floatx,
floaty);××函数的定义01函数的定义01例7.2:使用函数计算一个学生两门课程的总分和平均分。#include<stdio.h> floatsum(floatx1,floaty1) //定义求和函数
{returnx1+y1;}
floatavg(floatx2,floaty2) //定义求平均值函数
{return(x2+y2)/2;}
floatmain() {
floatscore1,score2;
floatsum1=0,avg1=0;
printf("请输入第一门课程的分数:");
scanf("%f",&score1);
printf("请输入第二门课程的分数:");
scanf("%f",&score2);
sum1=sum(score1,score2); //调用函数sum()
avg1=avg(score1,score2); //调用函数avg()
printf("该学生总分为%.2f,平均分为%.2f\n",sum1,avg1); }
在定义函数时,可以没有函数体,但花括号{}必须有,这样的函数称为空函数。在C语言中,所有的函数定义,包括主函数main在内,都是平行的。也就是说,在一个函数的函数体内,不能再定义另一个函数,即不能嵌套定义。注意:函数的定义01函数之间允许相互调用,也允许嵌套调用。一般将调用者称为主调函数。函数还可以自己调用自己,称为递归调用。main函数(主函数)可以调用其它函数,而不允许被其它函数调用main函数。一个C源程序必须有,也只能有一个主函数main。函数的定义01形式参数:在定义函数时函数名后面括弧中的变量名,简称形参。实际参数:在调用函数时函数名后面括弧中的表达式,简称实参。main(){inta,b,m;scanf(“%d,%d”,&a,&b);m=max(a,b);printf(“Maxis%d”,m);}实参表1.形式参数和实际参数intmax(intx,inty){intz;z=x>y?x:y;return(z);}形参表函数的参数和返回值02函数的形参和实参具有以下特点:(1)只有当函数被调用时,系统才给形参变量分配内存单元,在调用结束时,所分配的内存单元就被释放。(2)实参可以是常量、变量、表达式、函数等,无论实参是何种类型的量,在进行函数调用时,它们都必须具有确定的值,以便把这些值传送给形参。(3)实参和形参在数量上,类型上,顺序上应严格一致,否则会发生类型不匹配”的错误。(4)形参和实参的功能是作数据传送。发生函数调用时,主调函数把实参的值传送给被调函数的形参从而实现主调函数向被调函数的数据传送。函数的参数02函数的返回值
函数的数据类型就是函数返回值的类型,称为函数类型。
函数的返回值通过函数中的return语句将被调用函数中的一个确定的值带回到主调函数中去。
return一般形式:return(表达式);return表达式;return;03如:
int
max(intx,inty){int
z
;
z=x>y?x
:y
;
return
z;
}当没有返回值时,可以写成:return
;}返回结果不返回结果函数的返回值03注意:(1)
函数的返回值的类型应与函数的类型一致。如不一致,以函数类型为准,对返回值进行类型转换,
然后传送给调用函数。如:
intf(){return
3.5
;}
int
main()
{
inta=f();
/*a被初始化为3*/}函数的返回值03(2)
一个函数可以有多个return语句,但只可能执行其中一个。例:
intmax(intx,
inty){
if
(x>y)
returnx
;
else
returny
;}注意:函数的返回值03说明:(1)return后面的值可以是一个表达式,例如:
z=x>y?x:y;return(z);return(x>y?x:y);(2)若函数体内没有return语句,就一直将函数执行完,再返回调用函数,有一个不确定的值带回。(3)return后面可以无“返回值”(即return;),则该return语句只起到终止函数执行,返回主调函数的作用。函数的返回值03
函数的调用7.3
函数的调用函数的声明和函数调用函数的参数传递函数声明的一般格式:
类型说明符函数名(形式参数表列);函数的声明01用户自定义函数在调用后,必须对该函数进行声明。
函数声明就是函数原型。作用:将函数名、函数类型及形参个数、顺序、类型告诉编译器。编译器用函数原型测试函数调用是否正确。格式:
类型符函数名(类型形参1,···);
或:
类型符函数名(参数类型1,···);
float
max(float
x,float
y){return(x>y?x:y);}floatmax(floatx,floaty);2)函数原型要与函数的定义匹配,且如果在所有函数定义之前,在函数外部预先对各个函数进行了声明,则在调用函数中可缺省对被调用函数的声明。#include<stdio.h>函数的声明01intmain(){floata,b,c;scanf("%f%f",&a,&b);c=max(a,b);printf("maxis%f",c);}3)如果被调函数的定义出现在调用函数之前,可省略函数原型。
#include<stdio.h>
intmax(intx,inty){if(x>y)returnx;elsereturny;}intmain(){inta,b,c;scanf("%d%d",&a,&b);c=max(a,b);printf("max=%d\n",c);return0;}函数的声明014)函数的返回值是整型或字符型,可以不必进行声明。5)标准库函数在调用前,应使用宏定义。如在使用输入输出函数时,应在文件开头用:#include<stdio.h>使用字符串处理函数,应该用:#include<string.h>使用数学库中的函数,应该用:#include<math.h>所有的数学函数返回double型的
函数的声明01main(){inta;…f1(a);…}f1(intx){……}调用返回函数的使用是通过函数调用实现的。所谓函数调用就是调用函数向被调函数传送数据并将控制权交给被调用函数,当被调用函数执行完后,将结果回传给调用函数并交回控制权。下面来看怎么调用函数的调用02函数调用的一般形式实际参数简称“实参”,是一个具有确定值的表达式。函数在调用时,将实参的值赋给对应的形参。实参表中的实参个数、顺序及类型应与被调用函数中的形参一致。
intmain()
max(int
x,
int
y){int
a,b,c;
{
scanf(“%d,%d”,&a,&b);
intz=x>y?x:y;c=max(a,b);
return
z;
printf(“%d”,c);
}}
实
参形参函数的调用02
#include<stdio.h>floatfact(
intn
);
intmain(){
floatsum=0.0;
intk;for(k=1;k<=19;k+=2)sum=sum+fact(k)
;printf(“sum=%.1f\n”,sum);}例:求1!+3!+5!+…+19!(函数调用过程)
float
fact(intn){inti;floatt=1.0;
for(i=2;i<=n;i++)
t=t*i;returnt
;}函数的调用02调用方式:
1)函数表达式如:
c=2*max(a,
b);
2)作为语句用(函数语句)如:
printf(“****”);
fun();
3)作为函数参数如:
m=max(a,max(b,c));
printf(“%f\n”,
max(a,
b));函数的声明和函数调用格式02#include<stdio.h>intmain(){intfun(intx,inty);/*函数声明*/inti=2,j=6,s;
s=fun(i,j);/*函数调用*/printf("%d",s);return0;}intfun(intx,inty)
/*函数定义*/{
intz;z=x+y;returnz;}函数的调用02例
发生函数调用时,主调函数把实参的值传送给被调函数的形参从而实现主调函数向被调函数的数据传送。main(){inta,b,m;scanf(“%d,%d”,&a,&b);m=max(a,b);printf(“Maxis%d”,m);}intmax(intx,inty){intz;z=x>y?x:y;return(z);}实参表形参表函数的参数传递03c=max(a,b);-----------------------实参:在运行时
把函数的
max(intx,inty)把值传给函数。
结果赋给
{………
函数名
returu(z);形参:通知系统
}要预留内存位置。main(){inta,b,m;scanf(“%d,%d”,&a,&b);m=max(a,b);printf(“Maxis%d”,m);}intmax(intx,inty){intz;z=x>y?x:y;return(z);}实参表形参表函数的参数传递03函数被调用时,系统给形参变量分配内存单元,调用结束时,内存单元就被释放。(在内存中实参单元和形参单元是不同的存储单元。)无论实参是何种类型的量,在进行函数调用时,它们都必须具有确定的值。实参和形参在数量上,类型上,顺序上应相匹配。函数调用中发生的数据传送是单向的。即只能把实参的值传送给形参,不能把形参值反向传送给实参。因此,形参的值如发生改变,不会影响到调用函数中实参的值。几点说明:函数的参数传递03voidchange(intx,inty);intmain(){
inta,b;a=2;b=3;
change(a,b);printf(“a=%db=%d\n”,a,b);}例:调用函数,完成两个变量的交换。void
change(int
x,int
y)
{intt;t=x;x=y;y=t;
}2332特点:
单向传递,传递实参的值。实参与形参占用不同的存储单元23change函数xy(形参)2004H2000Hmain函数ab(实参)1000H1004H
输出结果:
a=2b=3#include<stdio.h>#include<stdio.h>intmain(){voidswap(int,int);inta,b;printf("请输入两个整数a,b:");scanf("%d%d",&a,&b);
swap(a,b);printf("a=%d,b=%d\n",a,b);
return0;}
voidswap(intx,inty){inttemp;temp=x;x=y;y=temp;
printf("x=%d,y=%d\n",x,y);}例7.4:调用函数时的参数传递。函数的参数传递03
函数的嵌套调用和递归调用7.4
函数的嵌套调用和递归调用函数的嵌套调用函数的递归调用函数的嵌套调用
在C语言中,不能将函数定义放在另一个函数的函数体中,但允许在调用一个函数的过程中调用另一个函数。这称为函数的嵌套调用。01main函数{:c=f1(a);:}f1(ints)函数{:d=f2(x,y);:}f2(intx,inty)函数{::}例7.6:用弦切法求方程x3-5x2+16x-80=0的根。函数的嵌套调用01用弦切法求方程f(x)=0的根的算法为:s1:在函数的定义域内取两点x1,x2,使f(x1)*f(x2)<0;s2:求两点(x1,f(x1)),(x2,f(x2))的连线与x轴的交点x;
x=(x1*f(x2)-x2*f(x1))/(f(x2)-f(x1))s3:判断f(x)<e(e为给定的很小的一个数),若成立,转s6;否则转s4;s4:判断f(x)*f(x1)<0,若成立,x2=x;否则x1=x;s5:转s2;s6:输出x,它即为所求的根。函数的嵌套调用01#include<stdio.h>#include<math.h>doublef(doublex){doubley;y=x*x*x-5*x*x+16*x-80;returny;}doublexpoint(doublex1,doublex2){doublez;z=(x1*f(x2)-x2*f(x1))/(f(x2)-f(x1));returnz;}函数的嵌套调用01doubleroot(doublex1,doublex2){doublex,y,y1;y1=f(x1);do{x=xpoint(x1,x2);y=f(x);if(y*y1>0){y1=y;x1=x;}elsex2=x;}while(fabs(y)>=0.00001);returnx;}函数的嵌套调用01intmain(){doublex1,x2,f1,f2,x;do{printf("Inputx1,x2:\n");scanf("%lf,%lf",&x1,&x2);f1=f(x1);f2=f(x2);}while(f1*f2>=0);x=root(x1,x2);printf("Arootofequationis%8.4lf\n",x);
return0;}main()root()xpoint()f()函数的嵌套调用01函数的递归调用概念:函数在它的函数体内直接或间接地调用它自身优点:可使程序简洁,提高程序的可读性。缺点:递归调用会增加存储空间和执行时间上的开销。02funa(){…funa();…}funb(){…func();
…}func(){…funb();
…}直接递归调用间接递归调用
一个问题要采用递归方法来解决时必须符合以下三个条件:(1)能将问题转化为一个新的问题,而这个新问题的解决方法与原问题的解法相同,它们只是有规律的递增或递减。(2)通过转化过程使问题得到解决。(3)要有一个明确的结束递归的条件。递归程序的执行过程可分为递推和回归两个阶段。在递推阶段,把较复杂的问题(规模为n)的求解推到比原问题简单一些的问题(规模小于n)的求解。在回归阶段,当获得最简单情况的解后,逐级返回,依次得到稍复杂问题的解。函数的递归调用02例7.7:编写一个递归函数,求n的阶乘值n! 若用fact(n)表示n的阶乘值,根据阶乘的数学定义可知:
函数的递归调用02longfact(intn){longm;if(n==0) m=1;else m=n*fact(n-1);return(m);}显然,当n>0时,fact(n)是建立在fact(n-1)的基础上。由于求解fact(n-1)的过程与求解fact(n)的过程完全相同,只是具体实参不同,因而只需借助递归机制进行自身调用即可。于是求n的阶乘值fact(n)的具体实现为:函数的递归调用02fact(5)fact(4)*5fact(3)*4fact(2)*3fact(1)*2fact(0)*1fact(0)=1fact(1)=1fact(2)=2fact(3)=6fact(4)=24fact(5)=120函数的递归调用02//作为函数参数#include<stdio.h>intmain(){printf(“%d”,fact(5));}主程序调用参考代码://作函数表达式#include<stdio.h>intmain(){ inta;
a=1*fact(5); printf("%d",a);}函数的递归调用02数组作函数参数7.5
数组作函数参数数组名作函数参数数组元素作函数参数二维数组作函数参数
数组元素作函数的参数与普通变量作函数的参数本质相同。数组元素作函数实参时,仅仅是将其代表的值作为实参处理。数组中元素作为函数的实参,与简单变量作为实参一样,结合的方式是单向的值传递。数组元素作函数的参数01#include<stdio.h>floatmax(floatx,floaty){if(x>y)returnx;elsereturny;}intmain(){ intk;floatm,a[]={12.34,123,-23.45,67.89,43,79,68,32.89,-34.23,10};m=a[0];/*假设第一个元素是最大值*/for(k=1;k<10;k++)/*循环9次*/
m=max(m,a[k]);/*调用max函数,实参m和a[k]给形参x,y*/printf("%5.2f\n",m);/*输出m的值*/
return0;}例7.9:求数组中的最大元素。数组元素作函数的参数01数组元素能否作函数的形参?数组元素作函数的参数01
用数组名作函数的参数可以解决函数只能有一个返回值的问题。数组名代表数组的首地址,在数组名作为函数的参数时,形参和实参都应该是数组名。在函数调用时,实参给形参传递的数据是实参数组的首地址,即实参数组和形参数组完全等同,是存放在同一存储空间的同一个数组,形参数组和实参数组共享存储单元。如果在函数调用过程中形参数组的内容被修改了,实际上也修改了实参数组的内容。数组名作函数参数02#include<stdio.h>voidinputdata(inta[],intn){inti;for(i=0;i<n;i++)
scanf("%d",&a[i]);}intmax(inta[],intn){inti,m;m=a[0];for(i=1;i<n;i++)if(m<a[i])m=a[i];returnm;}intmain(){intarray[10];
inputdata(array,10);printf(“最大值为:%d\n",max(array,10));return0;}例:求数组中的最大元素。数组名作函数参数02形参数组与实参数组之间的结合要注意以下几点:调用函数与被调用函数中分别定义数组,其数组名可以不同,但类型必须一致。形参数组与实参数组的结合是采用地址结合的,从而可以实现数据的双向传递。在被调用函数中改变了形参数组元素的值,实际上就改变了实参数组元素的值。数组作形参时,形参数组可以不指定一维数组的长度。数组名作函数参数02
多维数组名也可以作为函数的实参和形参。在定义函数时,对形参数组的说明可以指定每一维的大小,也可以省略第一维的大小。但是不能把多维数组的第二维及其他高维的大小说明省略。因为从实参传来的是数组起始地址,如果在形参中不说明列数,则系统无法决定应为多少行多少列,也就无法确定数组元素在内存中的位置。二维数组作为函数参数03#include<stdio.h>#defineM3#defineN3voidinputdata(inta[][N],intm){ inti,j;for(i=0;i<m;i++)for(j=0;j<N;j++)scanf("%d",&a[i][j]);}
例7.11:利用函数求两个矩阵的和并输出。自定义函数:用于输入
voidoutputdata(inta[][N],intm) { inti,j; for(i=0;i<m;i++) { for(j=0;j<N;j++)printf("%5d",a[i][j]); printf("\n"); } } voidsum(inta[][N],intb[][N],intc[][N],intm) { inti,j; for(i=0;i<m;i++) for(j=0;j<N;j++) c[i][j]=a[i][j]+b[i][j]; }自定义函数:用于输出自定义函数:用于求和intmain(){ intmatrix1[M][N],matrix2[M][N],matrix3[M][N];inputdata(matrix1,M);inputdata(matrix2,M);sum(matrix1,matrix2,matrix3,M);outputdata(matrix3,M);
return0;}主函数参数传递小结:实参形参传递数据基本变量常数表达式数组元素基本变量传值数组名数组传址变量的作用域与存储类别7.6
变量的作用域与存储类别变量的作用域变量的存储类别局部变量与全局变量
变量的作用域是指变量的可见范围或可使用的有效范围。变量的作用域可为一个函数,也可为整个程序。
C语言中变量说明的方式不同,其作用域也不同。C语言中的变量,按作用域范围可分为两种:局部变量和全局变量。1、局部变量在一个函数内部定义的变量是内部变量,它只在本函数范围内有效,即其作用域是有限的、局部的,称为局部变量。01说明:不同函数中可以使用相同名字的变量,它们代表不同的对象,互不干扰。floatf1(inta){intb,c;:}charf2(intx,inty){inti,j,b;:}intmain(){intm,n,j;:}函数f1a,b,c有效函数f2x,y,i,j,b有效main函数m,n,j有效局部变量与全局变量01说明:②函数的形参也是局部变量。③在一个函数内部,可以在复合语句中定义变量,这些变量只在本复合语句内有效,这种复合语句也称为“分程序”或“程序块”。局部变量与全局变量01intf(floatx){doubley;:{doublez;:}:}x,y的作用域z的作用域局部变量与全局变量01#include<stdio.h>intmain(){inta=1,b=2,c=3;printf("1---a=%d,b=%d,c=%d",a,b,c);{inta,b,c;a=10,b=20,c=30;printf("2---a=%d,b=%d,c=%d",a,b,c);}printf("3---a=%d,b=%d,c=%d",a,b,c);return0;}局部变量与全局变量01输出结果:1---a=1,b=2,c=32---a=10,b=20,c=303---a=1,b=2,c=3例7.12:复合语句中的局部变量2、全局变量
在函数外面定义的变量称为全局变量(外部变量)。全局变量可以被本文件中的其他函数所共有。其作用域为从定义处开始至本文件的结束。
在一个函数内,可以使用本函数中的局部变量,又可以使用有效的全局变量。局部变量与全局变量01说明:全局变量的作用:增加函数间数据联系的渠道。
由于同一文件的所有函数都可以引用全局变量的值,因此如果在一个函数中改变了全局变量的值,就能影响到其他函数,相当于各函数间有直接的传递通道。用全局变量可以从一个函数中返回多个值。局部变量与全局变量01#include<stdio.h>floatmax=0.0,min=0.0;//全局变量floataverage(floata,floatb,floatc,floatd);intmain(){
floatx1,x2,x3,x4,ave;scanf("%f,%f,%f,%f",&x1,&x2,&x3,&x4);ave=average(x1,x2,x3,x4);
printf(“Max=%.1f,Min=%.1f,Ave=%.1f\n",max,min,ave);
return0;}floataverage(floata,floatb,floatc,floatd){floatave;max=a;min=a;if(max<b)max=b;if(max<c)max=c;if(max<d)max=d;
if(min>b)min=b;
if(min>c)min=c;
if(min>d)min=d;
ave=(a+b+c+d)/4;
returnave;}例:输入4个数,编一个函数求这些数的平均值,最大数、最小数。局部变量与全局变量01#include<stdio.h>intmax(intx,inty){returnx>y?x:y;}intmain(){
externinta,b;//引用后面的全局变量a,b
printf("%d\n",max(a,b));
return0;}inta=13,b=8;若文件中的某一函数想引用定义在后面的全局变量,则应在该函数中用关键字extern作“外部变量说明”,表示该变量在函数的外部定义在函数内部可以使用它们。局部变量与全局变量01#include<stdio.h>inta=13,b=8;//全局变量a,bintmax(intx,inty){returnx>y?x:y;}intmain(){inta;//局部变量aa=34;printf("%d\n",max(a,b));
return0;}如果在同一个文件中,全局变量与局部变量同名,则在局部变量的作用范围内,全局变量不起作用。局部变量与全局变量01结果:34#include<stdio.h> intwater=1;
//全局变量watervoidFfire(intfire) //定义函数Ffire(){intwater=1;
//局部变量waterfire-=water;}voidmsg(intfire) //定义函数msg(){if(fire==0)
printf("火被扑灭啦!\n");else
printf("警报尚未解除!\n");
}intmain() {intfire=1; //主函数中的局部变量fireFfire(fire); //调用Ffire()printf("“远水”救“近火”?");msg(fire); //第一次调用msg(){intwater=1;//主函数复合语句中的局部变量waterintfire=1//主函数复合语句中的局部变量firefire-=water;printf("“近水”救“近火”?");msg(fire); //第二次调用msg()}msg(fire); //第三次调用msg()fire-=water; msg(fire); //第四次调用msg()return0; }例7.14:远水救不了近火(全局变量与局部变量同名的实例)。。局部变量与全局变量01用大模型找找远亲不如近邻的故事。静态存储方式:程序的运行期间分配固定的存储空间的方式。动态存储方式:程序运行期间根据需要进行动态的分配存储空间的方式。变量两个属性:数据类型和数据的存储类别。存储类别是指数据在内存中存储的方式。两种存储方式:静态存储类和动态存储类。四种存储类别:自动的(auto)、静态的(static)、寄存器的(register)、外部的(extern)。变量的存储类别02从变量的作用域(即从空间)局部变量全局变量从变量值存在的时间(即生存期)静态存储变量动态存储变量1、自动存储变量
自动存储变量是指这样一种变量,当程序模块执行时,系统自动为其分配存储空间,变量的值也存在,当程序模块执行完毕后,其值和存储空间也随之消失。
自动存储变量的定义格式为:
[auto]类型说明符变量名[=初值表达式],......;变量的存储类别02
在一般情况下,关键字[auto]可以省略。自动存储变量必须定义在函数体内,或为函数的形参。性质:1)作用域的有限性。自动存储变量是局部变量,其作用域为变量所在的函数或变量所在的分程序。2)生存期的短暂性。只有当程序模块执行时,本模块中的自动存储变量的值才存在,退出此模块时,本模块中的自动存储变量的空间被释放。3)可见性与存在性的一致性。4)独立性。5)未赋初值前的值无意义。变量的存储类别022、寄存器存储变量
寄存器存储变量具有与自动存储变量完全相同的性质。当将一个变量定义为寄存器存储类别时,系统将它存放在CPU中的一个寄存器中。通常将使用频率较高的变量定义为寄存器存储变量。
寄存器存储变量的定义格式为:
register类型说明符变量名变量的存储类别02ANSIC对寄存器存储类别只作为建议提出,不作硬性统一规定。一般将int和char型变量定义为寄存器变量。#include<stdio.h>intmain(){
registerintx=1;inner();printf("%d\n",x);return0;}voidinner(){
registerintx=2;
printf("%d\n",x);}变量的存储类别02结果:213、静态局部存储变量局部静态存储变量与自动存储变量一样,是定义在某一函数内的变量,但与自动存储变量不同的是局部静态存储变量的值不会因函数执行结束而消失。所以如果再次回到函数内执行时,上次执行后,局部静态存储变量的值仍然在。局部静态存储变量的定义格式:
static类型说明符变量名[=初始化常量表达式],......;性质:1)作用域的有限性。(同自动存储变量)2)值的永久性。3)可见性与存在性的不一致性。4)未初始化的值为0或‘\0’。变量的存储类别02#include<stdio.h>intmain(){voidincrement();
increment();increment();increment();
return0;}voidincrement(){
intx=0;x=x+1;printf("%d\n",x);}#include<stdio.h>intmain(){voidincrement();
increment();increment();increment();return0;}voidincrement(){
staticintx=0;x=x+1;printf("%d\n",x);}自动存储变量局部静态存储变量result:111result:
1
2
34、外部变量(全局变量):注:外部变量默认是静态存储类别,auto只能用于函数内部的变量
外部变量是指定义在文件中的所有函数之外的变量。在定义时,不必加关键字“extern”,但在声明时必须加“extern”关键字。性质:
1)作用域的全局性。
2)值的永久性。
3)可见性与存在性的一致性。
4)未初始化的值为0或‘\0’。变量的存储类别02#include<stdio.h>voidmain(){voidgx(),gy();
externintx,y;printf("1:x=%dy=%d\n",x,y);y=246;gx();gy();}voidgx(){
externx,y;x=135;printf("2:x=%dy=%d\n",x,y);}intx,y;//外部变量voidgy(){printf("3:x=%dy=%d\n",x,y);}1:x=0y=02:x=135y=2463:x=135y=246变量的存储类别02外部变量的两种情况:
1)限定只本文件内使用的外部变量。此时在定义时须加关键字static。通过声明可将外部变量的作用域可以在本文件内延伸。称为静态全局变量。2)可在本程序的其他文件中使用的外部变量。此时在定义时不能加关键字static。通过声明可将此外部变量的作用域延伸到本程序的其他文件中。
这两种情况下,对外部变量的作用域进行延伸时都必须对外部变量进行声明,声明方式为:
extern
类型说明符变量名;变量的存储类别02file1.c#include<stdio.h>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.cexterninta;intpower(intn){inti,y=1;for(i=1;i<=n;i++)y=y*a;returny;}外部变量的作用域在不同文件中延伸变量的存储类别023)静态局部变量和静态全局变量的区别定义的位置不同静态局部变量在函数内定义静态全局变量在函数外定义作用域不同静态全局变量为定义它的源文件内静态局部变量仅限于定义它的函数内变量的存储类别02编译预处理7.7
执行结果编辑编译连接运行源代码,源程序(*.c)目标文件(*.obj)可执行文件(*.exe)编译预处理编译预处理
ANSIC标准规定可以在C源程序中加入一些“预处理命令”,以改进程序设计环境,提高编程效率。
预处理命令是由ANSIC统一规定的,但是它不是C语言本身的组成部分,不能直接对它们进行编译。必须在对程序进行通常的编译之前,先对程序中这些特殊的命令进行“预处理”。
经过预处理后程序可由编译程序对预处理后的源程序进行通常的编译处理。编译预处理编译预处理概念:在进行编译的第一遍扫描(词法扫描和语法分析)之前所作的工作。特点所有预处理命令均以#开头每条命令独占一行命令不以“;”为结束符作用范围仅限于说明它们的那个文件C提供的预处理功能主要有以下特点:编译预处理文件包含宏定义条件编译宏定义1、无参宏定义定义格式:#define标识符字符串“#”表示这是一条预处理命令define为宏定义命令宏名宏体:可以是常数、表达式等02功能:用指定标识符(宏名)代替字符序列(宏体)#definePI3.1415926#defineM3+5#defineX(y*y+3*y)宏展开:编译预处理时,用宏体替换宏名---不作语法检查说明:宏名一般用大写字母表示。宏定义是用宏名替换一个字符串,不作任何检查。在宏定义时,可以使用已经定义的宏名。即宏定义可以嵌套,可以层层替换。在预处理时,要进行宏展开。宏展开时,只是将程序中的宏名用宏体进行替换,不会做任何运算。宏定义不是C语句,不必在行末加分号。如果加了分号则会连分号一起进行替换。对程序中用双撇号括起来的字符串内的字符,即使与宏名相同,也不进行替换。宏定义01#include<stdio.h>#definePI3.1415926voidmain(){doubler,len,s,v;printf("请输入半径:");scanf("%lf",&r);len=2*PI*r; s=PI*r*r;
v=PI*r*r*r*4/3;
printf("len=%.2lf,s=%.2lf,v=%.2lf\n",len,s,v);}例7.18:输入圆的半径,求圆的周长、面积和球的体积。要求使用无参宏定义圆周率。引用无参宏求面积引用无参宏求体积引用无参宏求周长PI是宏名,3.1415926用来替换宏名的常数2、带参数的宏定义定义格式:#define宏名(形参表)字符串字符串中包含有括号中所指定的参数#defineM(y)y*y+3*y/*宏定义*/k=M(5);/*宏调用*/调用格式:宏名(实参表);宏定义01例:带参数的宏定义与宏调用。#include<stdio.h>#defineMAX(a,b)(a>b)?a:bvoidmain(){intx,y,max;printf("inputtwonumbers:");scanf("%d%d",&x,&y);
max=MAX(x,y);printf("max=%d\n",max);}宏定义宏调用宏定义01例7.19:输入圆的半径,求圆的周长、面积,并根据公式求同半径球的体积,要求使用带参宏定义完成。 #include<stdio.h> #include<stdlib.h> #definePI3.1415926 #defineLEN(x)2*PI*x //第4~6行分别定义了带参的宏LEN、S和V #defineS(x)PI*x*x #defineV(x)4*PI*x*x*x/3 intmain() { doubler,len,s,v; printf("请输入半径:");
scanf("%lf",&r); len=LEN(r);//第12~14行分别调用了带参的宏LEN、S和V s=S(r); v=V(r); printf("周长=%.7lf\n面积=%.7lf\n体积=%.7lf\n",len,s,v); system("pause"); return0; } 宏定义01说明:①在宏定义中的形参是标识符,而宏调用中的实参可以是表达式。②在带参宏定义中,形参不作类型定义。而宏调用中的实参有具体的值,必须作类型说明。③在宏定义中,字符串内的形参通常要用括号括起来以避免出错。宏定义01例:defineh(x)x*x
调用时:h(a+b)展开时:a+b*a+b(与原意不符)改为:defineh(x)(x)*(x)
调用时:h(a+b)展开时:(a+b)*(a+b)④定义带参宏时,宏名与左括号之间不能留空格。⑤较长的定义在一行中写不下时,可在本行末尾使用反斜杠表示续行。3、取消宏定义如果需要在源程序的某处终止宏定义,则需要使用#undef命令取消宏定义。格式:#undef标识符定义的宏名宏定义01#include<stdio.h>#definePI3.14159voidmain(){floatr=10.0;floatb,c,d;b=PI*r;#undefPI c=PI*r*r;
printf("r=%6.2f\n",r);printf("b=%6.2f\nc=%6.2f\n",b,c);}此程序编译时会出错。取消宏定义宏定义01文件包含概念:一个源文件可以将另一个源文件的全部内容包含进来,即将另外的文件包含到本文件之中。格式:#include“包含文件名”#include<包含文件名>或 把指定的文件插入该命令行位置取代该命令行,从而把指定的文件和当前的源程序文件连成一个源文件。02文件包含02说明:包含命令中的文件名可以用双引号括起来,也可以用尖括号括起来。尖括号:只在包含文件目录中查找双引号:先在当前原文件目录中查找,未找到再在包含文件目录中查找一个include命令只能指定一个被包含文件文件包含允许嵌套,即在一个被包含的文件中又可以包含另一个文件。在包含文件中不能有main函数。文件包含02条件编译三种形式的条件编译#ifdef标识符程序段1#else
程序段2#endif如果标识符已被#define命令定义过则对程序段1进行编译;否则对程序段2
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025年绍兴市上虞区中医医院医共体招聘编外人员5人模拟笔试试题及答案解析
- 2025年福建泉州惠安县宏福殡仪服务有限公司招聘5人参考考试试题及答案解析
- 2025年杭州市上城区闸弄口街道社区卫生服务中心招聘编外1人考试参考试题及答案解析
- 深度解析(2026)GBT 26103.5-2010NGCLZ型带制动轮鼓形齿式联轴器
- 2025浙江宁波市象山半边山紫冠投资有限公司酒店管理分公司(宁波象山海景皇冠假日酒店)招聘3人参考考试题库及答案解析
- 深度解析(2026)《GBT 25982-2024客车车内噪声限值及测量方法》(2026年)深度解析
- 2025四川德阳市旌阳区孝泉镇卫生院(旌阳区第二人民医院)招聘2人备考笔试题库及答案解析
- 深度解析(2026)《GBT 25796-2010反应艳黄W-2G(C.I.反应黄39)》
- 深度解析(2026)《GBT 25734-2010牦牛肉干》(2026年)深度解析
- 深度解析(2026)《GBT 25688.2-2010土方机械 维修工具 第2部分:机械式拉拔器和推拔器》
- 2025至2030中国聚四氟乙烯(PTFE)行业经营状况及投融资动态研究报告
- 教育、科技、人才一体化发展
- 营销与客户关系管理-深度研究
- 耐压试验操作人员岗位职责
- 2020-2021学年广东省广州市黄埔区二年级(上)期末数学试卷
- 财政部政府采购法律法规与政策学习知识考试题库(附答案)
- 长鑫存储在线测评题
- DL∕T 5344-2018 电力光纤通信工程验收规范
- T-CCIIA 0004-2024 精细化工产品分类
- 世界当代史教材
- 高压电动机保护原理及配置
评论
0/150
提交评论