《C语言第七章ppt教学》由会员分享,可在线阅读,更多相关《C语言第七章ppt教学(107页珍藏版)》请在金锄头文库上搜索。
1、卢卢 嫣嫣本章要点本章要点主要内容主要内容 7.1 7.1 概述概述 7.7.函数定义的一般形式函数定义的一般形式 7.7.函数参数和函数的值函数参数和函数的值 7.7. 函数的调用函数的调用 7.7. 函数的嵌套调用函数的嵌套调用 7.7.函数的递归调用函数的递归调用 7.7.数组作为函数参数数组作为函数参数 7.8 7.8 局部变量和全局变量局部变量和全局变量 7.7.变量的存储类别变量的存储类别 7.10 7.10 内部函数和外部函数内部函数和外部函数 7.1 概述 一个程序可由一个主函数和若干个其他函数构成。一个较大的程序可分为若干个程序模块,每一个模块用来实现一个特定的功能。在高级语
2、言中用子程序实现模块的功能。子程序由函数来完成。 函数间的调用关系:由主函数调用其他函数,其他函数也可以互相调用。同一个函数可以被一个或多个函数调用任意多次。例例7.17.1先举一个函数调用的简单例子先举一个函数调用的简单例子# include void main() void printstar(); /*对对printstar函数声明函数声明*/ void print_message(); /*对对print_message函数声明函数声明*/ printstar(); *调用调用printstar函数函数* print_message(); /*调用调用print_message函数函数
3、*/ printstar(); *调用调用printstar函数函数*/ void printstar() *定义定义printstar函数函数* printf(* * * * * * * * * * * * * * * *n);void print_message() *定义定义print_message函数函数* printf(How do you do!n); 运行情况如下:运行情况如下:* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *How do you do!How do you do!* * * * * *
4、* * * * * * * * * * * * * * * * * * * * * * * * * 说明:说明: (1)(1)一一个个程程序序由由一一个个或或多多个个程程序序模模块块组组成成,每每一一个个程程序序模模块块作作为为一一个个源源程程序序文文件件。对对于于较较大大的的程程序序,通通常常将将程程序序内内容容分分别别放放在在若若干干个个源源文文件件中中,再再由由若若干干源源程程序序文文件件组组成成一一个个C C程程序序。这这样样便便于于分分别别编编写写、分分别别编编译译,提提高高调调试试效效率率。一个源程序文件可以为多个一个源程序文件可以为多个C C程序公用。程序公用。(2) (2) 一
5、一个个源源程程序序文文件件由由一一个个或或多多个个函函数数以以及及其其他他有有关关内内容容(如如命命令令行行、数数据据定定义义等等)组组成成。一一个个源源程程序序文文件件是是一一个个编编译译单单位位,在在程程序序编编译译时时是是以以源源程程序序文文件件为为单单位位进进行行编编译译的的,而而不不是是以以函数为单位进行编译的。函数为单位进行编译的。(3) 程程序序的的执执行行是是从从main函函数数开开始始的的,如如果果在在main函函数数中中调调用用其其他他函函数数,在在调调用用后后流流程程返返回回到到main函函数数,在在main函函数数中中结结束束整整个个程程序序的的运运行。行。(4)所所有
6、有函函数数都都是是平平行行的的,即即在在定定义义函函数数时时是是分分别别进进行行的的,是是互互相相独独立立的的。一一个个函函数数并并不不从从属属于于另另一一函函数数,即即函函数数不不能能嵌嵌套套定定义义。函函数数间间可可以以互互相相调调用用,但但不不能能调调用用main函数。函数。main函数是系统调用的。函数是系统调用的。(5)(5)从用户使用的角度看,函数有两种:从用户使用的角度看,函数有两种: 标标准准函函数数,即即库库函函数数。这这是是由由系系统统提提供供的的,用用户户不不必必自自己己定定义义这这些些函函数数,可可以以直直接接使使用用它它们们。不不同同的的C C系系统统提提供供的的库库
7、函函数数的的数数量量和和功功能能会会有有一一些些不不同同,但但许许多多基基本本的的函数是共同的。函数是共同的。 用用户户自自己己定定义义的的函函数数。用用以以解解决决用用户户的的专门需要。专门需要。 (6) (6) 从函数的形式看,函数分两类:从函数的形式看,函数分两类: 无无参参函函数数。无无参参函函数数一一般般用用来来执执行行指指定定的的一一组组操操作作。在在调调用用无无参参函函数数时时,主主调调函函数不向被调用函数传递数据。数不向被调用函数传递数据。有有参参函函数数。主主调调函函数数在在调调用用被被调调用用函函数数时,通过参数向被调用函数传递数据。时,通过参数向被调用函数传递数据。 7
8、7. .函数定义的一般形式函数定义的一般形式 7.2.1 7.2.1 无参函数的定义一般形式无参函数的定义一般形式 定义无参函数的一般形式为定义无参函数的一般形式为:类型标识符类型标识符函数名函数名()() 声明部分声明部分 语句部分语句部分 7.2.2 7.2.2 有参函数定义的一般形式有参函数定义的一般形式 定义有参函数的一般形式为定义有参函数的一般形式为:类型标识符类型标识符函数名函数名(形式参数表列)(形式参数表列) 声明部分声明部分 语句部分语句部分 例如:例如: (intint ,intint ) ;/ */ *函数体中的声明部分函数体中的声明部分* *?; returnretur
9、n();); 7 7.2.3 .2.3 空函数空函数 定义空函数的一般形式为定义空函数的一般形式为:类型标识符类型标识符函数名函数名()() 例如:例如:()() 主调函数调用空函数时,只表主调函数调用空函数时,只表明这里要调用一个函数,但函明这里要调用一个函数,但函数本身什么工作也不做等数本身什么工作也不做等, ,以后以后扩充函数功能时补充上。扩充函数功能时补充上。 7 7. .函数参数和函数的值函数参数和函数的值 7 7. . .形式参数和实际参数形式参数和实际参数 形式参数:形式参数:函数名后面括号中的变量名称为函数名后面括号中的变量名称为“形式参数形式参数”(简称(简称“形参形参”)。
10、)。实际参数:实际参数:主调函数中调用一个函数时,函主调函数中调用一个函数时,函数名后面括号中的参数数名后面括号中的参数( (可以是一个表达式可以是一个表达式) )称为称为“实际参数实际参数”(简称(简称“实参实参”)。)。函数返回值:函数返回值:returnreturn后面的括号中的值作为后面的括号中的值作为函数带回的值(称函数带回的值(称函数返回值函数返回值)。)。 主调函数和被调用函数之间有数据传主调函数和被调用函数之间有数据传递的关系。在不同的函数之间传递数据,递的关系。在不同的函数之间传递数据,可以使用的方法有:可以使用的方法有:参数:通过形式参数和实际参数参数:通过形式参数和实际参
11、数返回值:用返回值:用returnreturn语句返回计算结果语句返回计算结果全局变量:外部变量全局变量:外部变量例例7.调用函数时的数据传递调用函数时的数据传递#include #include void void ()() intint max(intmax(int ,intint ) ); /* /* 对函数的声明对函数的声明 * */ / intint ,;,; scanfscanf(,);,); (,);(,); printfprintf( ,);,); intint max(intmax(int ,intint ) )* *定义有参函数定义有参函数max *max * intint
12、 ; ?; returnreturn();); 运行情况如下:运行情况如下:, 通过函数调用,可使两个函数中的数据发生联系。通过函数调用,可使两个函数中的数据发生联系。关于形参与实参的说明:关于形参与实参的说明:(1 1) 在定义函数中指定的形参,在未出现函数调用时,它们并不占内存中的存储单元。只有在发生函数调用时,函数max中的形参才被分配内存单元。在调用结束后,形参所占的内存单元也被释放。(2 2) 实参可以是常量、变量或表达式,例如:例如: max(,);但要求它们有确定的值。在调用时将实参的值赋给形参。(3 3)在被定义的函数中,必须指定形参的类型。(4 4)实参与形参的类型应相同或赋
13、值兼容。(5 5)值传递:实参向形参的数据传递是单向“值传递”,只能由实参传给形参,而不能由形参传回来给实参。 在调用函数时,给形参分配存储单元,并将实参对应的值传递给形参,调用结束后,形参单元被释放,实参单元仍保留并维持原值。 7.3.2 7.3.2 函数的返回值函数的返回值 函数的返回值函数的返回值是通过函数调用使主调函数得到的确定值。例如例如: :例7.中,max(,)的值是,max(,)的值是5。赋值语句将这个函数值赋给变量。 说明:说明: (1)函数的返回值是通过函数中的return语句获得的。 一个函数中可以有一个以上的return语句,执行到哪一个return语句,哪一个语句起作
14、用。return语句后面的括弧也可以不要例如例如: “return ;” 等价于等价于 “return (););”return后面的值可以是一个表达式。例如例如: (int ,int ) return(?:);); (2)函数的返回值应当属于某一个确定的类型,在定义函数时指定函数返回值的类型。例如例如:下面是下面是3个函数的首行:个函数的首行:int max(float ,float ) /* 函数值为整型 */char letter(char c1,char c2) /* 函数值为字符型 */ double min(int ,int ) /* 函数值为双精度型 */ 注意:注意:凡不加类型
15、说明的函数,自动按整型处理。(3)在定义函数时指定的函数类型一般应该和return语句中的表达式类型一致。 如果函数值的类型和return语句中表达式的值不一致,则以函数类型为准。 对数值型数据,可以自动进行类型转换。即函数类型决定返回值的类型。(4)对于不带回值的函数,应当用“void”定义函数为“无类型”(或称“空类型”)。此时在函数体中不得出现return语句。 例例 7.7. 返回值类型与函数类型不同返回值类型与函数类型不同# include void main()() int (float ,float ); float ,;,; int ; scanf(,);,); (,);(,)
16、; printf( ,);,); int max(float ,float ) float ; /* z为实型变量为实型变量 */ ? ; return();); 运行情况如下:, Max is 7.7. 函数的调用函数的调用 7.7.1 .1 函数调用的一般形式函数调用的一般形式函数调用的一般形式为函数调用的一般形式为: : 函数名(实参表列)函数名(实参表列)说明说明: :(1 1)如果是调用无参函数,则如果是调用无参函数,则“实参表列实参表列”可可以没有,但括弧不能省略。以没有,但括弧不能省略。(3 3)如果实参表列包括多个实参,对实参求值的顺序并不是确定的,有的系统按自左至右顺序求实参
17、的值,有的系统则按自右至左顺序。(2 2)如果实参表列包含多个实参,则各参数间用逗号隔开。实参与形参的个数应相等,类型应匹配。实参与形参按顺序对应,一一传递数据。 7.4.2 7.4.2 函数调用的方式函数调用的方式函数语句函数语句把函数调用作为一个语句。这时不要求函数带回值,只要求函数完成一定的操作。函数表达式函数表达式函数出现在一个表达式中,这种表达式称为函数表达函数表达式式。这时要求函数带回一个确定的值以参加表达式的运算。例如:* *(,);(,); 按按函函数数在在程程序序中中出出现现的的位位置置来来分分,可可以以有有以以下三种函数调用方式:下三种函数调用方式: 函数参数函数参数函数调
18、用作为一个函数的实参。例如例如: : m = max (a , max ( b , c ) ) ;其中max ( b , c )是一次函数调用,它的值作为max另一次调用的实参。m的值是a、b、c三者中的最大者。7.4.3 7.4.3 对被调用函数的声明和函数原型对被调用函数的声明和函数原型1.首先被调用的函数必须是已经存在的函数(是库函数或用户自己定义的函数)。但光有这一条件还不够。3.如果使用用户自己定义的函数,而该函数的位置在调用它的函数(即主调函数)的后面,应该在主调函数中对被调用的函数作声明。2.如果使用库函数,还应该在本文件开头用#include 命令将调用有关库函数时所需用到的信
19、息“包含”到本文件中来。 函数原型的一般形式为函数原型的一般形式为: :1. 函数类型 函数名(参数类型1,参数类型2);2. 函数类型 函数名(参数类型1,参数名1,参数类型2,参数名2); 声明的作用是把函数名、函数参数的个数和声明的作用是把函数名、函数参数的个数和参数类型等信息通知编译系统,以便在遇到函数参数类型等信息通知编译系统,以便在遇到函数调用时,编译系统能正确识别函数并检查调用是调用时,编译系统能正确识别函数并检查调用是否合法(例如函数名是否正确,实参与形参的类否合法(例如函数名是否正确,实参与形参的类型和个数是否一致),它不包含函数体。型和个数是否一致),它不包含函数体。注意:
20、注意: 函数的函数的“定义定义”和和“声明声明”的区别:的区别:l 函数的定义是指对函数功能的确立,包括指定函函数的定义是指对函数功能的确立,包括指定函数名,函数值类型、形参及其类型、函数体等,它数名,函数值类型、形参及其类型、函数体等,它是一个完整的、独立的函数单位。是一个完整的、独立的函数单位。l 函数的声明的作用则是把函数的名字、函数类型函数的声明的作用则是把函数的名字、函数类型以及形参的类型、个数和顺序通知编译系统,以便以及形参的类型、个数和顺序通知编译系统,以便在调用该函数时系统按此进行对照检查。在调用该函数时系统按此进行对照检查。 说明:说明:(1)如果被调用函数的定义出现在主调函
21、数之前,可)如果被调用函数的定义出现在主调函数之前,可以不必加以声明。因为编译系统已经先知道了已定义以不必加以声明。因为编译系统已经先知道了已定义函数的有关情况,会根据函数首部提供的信息对函数函数的有关情况,会根据函数首部提供的信息对函数的调用作正确性检查。的调用作正确性检查。(2)如果已经有文件的开头(在所有函数之前),已)如果已经有文件的开头(在所有函数之前),已对本文件中所调用的函数进行了声明,则在各函数中对本文件中所调用的函数进行了声明,则在各函数中不必对其所调用的函数再作声明。不必对其所调用的函数再作声明。如:如:#include char letter(char,char); fl
22、oat f(float,float); int i(float,float); void main() char letter(char c1,char c2) 例例7. 对被调用的函数作声明对被调用的函数作声明# include void main()() float add(float x, float y);); *对被调用函数对被调用函数add的声明的声明* float a,b,c; scanf(f,f,a,b);); cadd(a,b); printf(sum is f n,c););float add(float ,float ) *函数首部函数首部* float ; /* 函数体
23、函数体 */ z; return(z);); 例例7 7 对被调用的函数作声对被调用的函数作声明明# include float add(float ,float ) *函数首部函数首部* float ; /* 函数体函数体 */ z; return(z);); void main()() float a,b,c; scanf(f,f,a,b);); cadd(a,b); printf(sum is f n,c);); 7.7. 函数的嵌套调用函数的嵌套调用嵌套定义就是在定义一个函数时,其函数体内又包含另一个函数的完整定义 。输入输入4个整数,找出其中最大的数。用函数的嵌套调用来处理。个整数,
24、找出其中最大的数。用函数的嵌套调用来处理。#include void main()int max_4 int a,int b,int c,int d);int a,b,c,d,max;printf(“please enter 4 interger numbers:”);scanf(“%d%d%d%d”,&a,&b,&c,&d);max=max_4(a,b,c,d);printf(“max=%dn”,max);int max_4(int a,int b,int c,int d)int max_2(int,int);int m;m=max_2(a,b);m=max_2(m,c);m=max_2(m
25、,d);return(m);int max_2(int a,int b)if (ab) return a;else return b; 7.6 7.6 函数的递归调用函数的递归调用 在调用一个函数的过程中又出现直接或间接地调用该函数本身,称为函数的递归调用。语言的特点之一就在于允许函数的递归调用。例如:例如: int f ( int ) ,; (); return(*); 例例 7.6: 7.6: 有个人坐在一起,问第个人多少岁?他有个人坐在一起,问第个人多少岁?他说比第个人大岁。问第个人岁数,他说比第说比第个人大岁。问第个人岁数,他说比第个人大岁。问第个人,又说比第个人大岁。个人大岁。问第个
26、人,又说比第个人大岁。问第个人,说比第个人大岁。最后问第个人,问第个人,说比第个人大岁。最后问第个人,他说是岁。请问第个人多大。他说是岁。请问第个人多大。 age(5)= age (4)+2age(4)= age (3)+2age(3)= age (2)+2age(2)= age (1)+2age(1)= 10用数学公式表述如下:用数学公式表述如下:age(n)= 10 ()age(n-1)+2 ()可以用一个函数来描述上述递归过程:可以用一个函数来描述上述递归过程:int age(int ) *求年龄的递归函数求年龄的递归函数* int ; * 用作存放函数的返回值的变量用作存放函数的返回值
27、的变量 * if() ; else ();(); return(););运行结果如下:运行结果如下: 用一个主函数调用用一个主函数调用age函数,求得第函数,求得第5人的年龄。人的年龄。#include void main()() printf(,age();(); 例例7.77.7用递归方法求!用递归方法求! 求!也可以用递归方法,即!等于!求!也可以用递归方法,即!等于!,而!而!。!。可用下面的递归公式表示:可用下面的递归公式表示: ! (,)(,) ()!()! ()()#include #include void main()void main()long long fac(intf
28、ac(int n); n);intint n;longn;long intint y; y;printf(printf(“inputinput an an intergerinterger number: number:”););scanf(scanf(“%d%d”,&n,&n););y=y=fac(nfac(n););printf(printf(“%d%d!=%ld!=%ldn n”,n,y,n,y););long long fac(intfac(int n) n)long f;long f;if (n0)if (n0)printf(printf(“n n0,data error!0,dat
29、a error!”););else else if(nif(n=1) f=1;=1) f=1; else f=fac(n-1)*n; else f=fac(n-1)*n;return f;return f;#include void main() int eat(int n); int i,s; i=10; s=eat(i); printf(%dn,s); int eat(int n)int s; if (n=1) s=1; else s=(eat(n-1)+1)*2; return s;猴子吃桃问题。狮子第一天摘下若干个桃子,当即吃了一半,还不过瘾,猴子吃桃问题。狮子第一天摘下若干个桃子,当
30、即吃了一半,还不过瘾,又多吃一个。第二天早上将剩下的桃子吃掉一半,又多吃了一个。以后又多吃一个。第二天早上将剩下的桃子吃掉一半,又多吃了一个。以后每天早上都吃了前一天剩下的一半零一个。到第每天早上都吃了前一天剩下的一半零一个。到第10天早上想再吃时,就天早上想再吃时,就只剩下一个桃子了。求第一天共摘多少个桃子。只剩下一个桃子了。求第一天共摘多少个桃子。例例7.9 Hanoi(汉诺塔)问题汉诺塔)问题:由上面的分析可知:将个盘子从座移到座可以分解为以下3个步骤:1.将上个盘借助座先移到座上。2.把座上剩下的一个盘移到座上。3.将个盘从座借助于座移到座上。程序如下:程序如下:#include vo
31、id main()void hanoi(int n,char one,char two,char three); /* 对对hanoi函数的声明函数的声明 */ int m; printf(input the number of diskes:); scanf(“%d”,&m); printf(The step to moveing %d diskes:n,m); hanoi(m,A,B,C); void hanoi(int n,char one,char two,char three) /* 定义定义hanoi函数函数,将个盘从将个盘从one座借助座借助two座,移到座,移到three座座
32、*/ void move(char x,char y); /* 对对move函数的声明函数的声明 */ if(n=1) move(one,three); else hanoi(n-1,one,three,two); move(one,three); hanoi(n-1,two,one,three); void move(char x,char y) /* 定义定义move函数函数 */ printf(“%c-%cn,x,y); 运行情况如下:运行情况如下:input the number of diskes:3 The steps to noving 3 diskes: 7.7.数组作为函数参
33、数数组作为函数参数 7.7.1 7.7.1 数组元素作函数实参数组元素作函数实参 由于实参可以是表达式,而数组元素可以是表达式的组成部分,因此数组元素可以作为函数的实参,与用变量作实参一样,是单向传递,即“值传送”方式。 例例 7.107.10 有有两两个个数数组组和和,各各有有个个元元素素,将将它它们们对对应应地地逐逐个个相相比比(即即与与比比,与与比比)。如如果果数数组组中中的的元元素素大大于于数数组组中中的的相相应应元元素素的的数数目目多多于于b b数数组组中中元元素素大大于于a a数数组组中中相相应应元元素素的的数数目目( (例例如如,a ai ibbi i6 6次次,b bi iaa
34、i i3 3次次,其其中中i i每每次次为为不不同同的的值值) ),则则认认为为a a数数组组大大于于b b数数组组,并并分分别别统统计计出出两两个个数数组组相相应应元元素大于、等于、小于的次数。素大于、等于、小于的次数。 #include void main()() int large(int x,int y); /* 函数声明函数声明 */ int 10,10,,;,; printf(enter array a );); for(;) scanf(,);,); printf();); printf( enter array );); for(;) scanf (,);,); printf(
35、);); for(;);) if(large (i,i )= ) ; else if( large (i,i )=) =+; else ;printf(aibi %d timesnai=bi %d timesnaik) printf(array a is larger than array bn); else if (nk) printf(array a is smaller than array bn); else printf(array is equal to array bn);large(int ,int ) int ; if(););else if()flag;else flag;
36、 return(flag););运行情况如下:运行情况如下: enter array a: 5 3 8 9 1 3 5 6 0 4 array a is smaller thann array b 7.7.2 7.7.2 数组名作函数参数数组名作函数参数 用数组名作函数参数时,此时形参应当用数组名或用指针变量 。例例7.11 有一个一维数组有一个一维数组score,内放内放10个学生成绩,个学生成绩,求平均成绩。求平均成绩。#include void main()() float average(float array10); /* 函数声明函数声明 */ float score10 , av
37、er; int ; printf(input scores:);); for(; scanf(,score);); printf();); averaverage( score );); printf ( average score is .n, aver););float average (float array10) int ; float aver,; for (;);return(aver););运行情况如下:运行情况如下:input scores: .5 .5 average score is 83.40 例例 7.7.27.7.2形参数组不定义长度形参数组不定义长度#include
38、 void main()() float average(float ,int ) float score_15 , .,; float score_210= 67.5,89.5,99,6.5, 77,89.5,76.5,54,60,99.5; printf(“the average of class A is %6.2fn”, average(score_1,5); printf(“the average of class B is %6.2fn”, average(score_2,10); float average(float ,int ) int ; float aver,; for(
39、; sumsumarray; aversum; return();); 运行结果如下:运行结果如下:the average of class A is 80.40The average of class is 78.20例例 7.13 用选择法对数组中用选择法对数组中1010个整数按由小个整数按由小到大排序。到大排序。 所谓选择法就是先将10个数中最小的数与a0对换;再将a1到a9中最小的数与a1对换每比较一轮,找出一个未经排序的数中最小的一个。共比较9轮。未排序时的情况:a0 a1 a2 a3 a4 3 6 1 9 4 将5个数中最小的数1与a0对换: 1 6 3 9 4 将余下的4个数中最
40、小的数3与a1对换 1 3 6 9 4 将余下的3个数中最小的数4与a2对换 1 3 4 9 6 将余下的2个数中最小的数6与a3对换 1 3 4 6 9 程序:程序:#include void main()() void sort(int ,int ); int ,;,; printf(enter the array);); for(;) scanf(,);,); sort(,);,); printf(the sorted array );); for(; printf(,);,); printf();); void sort(int array,int )/*排序函数排序函数*/ int
41、,;,; for(;);) ; for(;);) if(array array=; =arrayk; arrayk=arrayi;arrayi=t 7.7.3. 7.7.3. 多维数组名作函数参数多维数组名作函数参数程序:程序:#include void main() max_value ( int 4); int 34=1,3,5,7,2,4,6,8,15,17,34,12; printf(max value is , max_value(a) ); 用多维数组名作为函数实参和形参。在被调函数中对形参数组定义时可以指定每一维的大小 。max_value ( int array 4) int
42、,max; max=; for(=;);) for(;if(array) max= array ; return(max);); 运行结果如下:运行结果如下:Max value is 34 7.87.8局部变量和全局变量局部变量和全局变量 7 7.8.1.8.1局部变量局部变量内部变量:在一个函数内部定义的变量称内部变量。它只在本函数范围内有效,即:只有在本函数内才能使用这些变量,故称为“局部变量” 。例:例:float f1( int a) /*函数函数f1 */int b,c; /* a、b、c有效有效*/ char f2(int x,int y) /*函数函数f2 */int i,j;
43、/* x、y、i、j有效有效*/ void main( ) /*主函数主函数*/int m,n; /* m、n有效有效*/ (1)主函数中定义的变量只在主函数中有效,而不因为在主函数中定义而在整个文件或程序中有效。主函数也不能使用其他函数中定义的变量。(2) 不同函数中可以使用相同名字的变量,它们代表不同的对象,互不干扰。(3) 形式参数也是局部变量。(4) 在一个函数内部,可以在复合语句中定义变量,这些变量只在本复合语句中有效,这种复合语句也称为“分程序”或“程序块”。void main ( )int a,b;int c; c=a+b; c在此范围内有效在此范围内有效 a,b在此范围内有效在
44、此范围内有效 7.8.2 7.8.2 全局变量全局变量外部变量:函数之外定义的变量称为外部变量。外部变量可以为本文件中其他函数所共用。它的有效范围为从定义变量的位置开始到本源文件结束。所以也称全程变量。int p=1,q=5; /* 外部变量外部变量 */float f1(int a) /* 定义函数定义函数f1 */int b,c;char c1,c2; /* 外部变量外部变量*/char f2 (int x, int y) /* 定义函数定义函数f2 */int i,j; 全局变量全局变量p,q的作用范围的作用范围 全局变量全局变量c1,c2的作用范围的作用范围void main ( )
45、/*主函数主函数*/int m,n; 例例7.15 7.15 有一个一维数组,内放个学生成绩有一个一维数组,内放个学生成绩,写一个函数,求出平均分、最高分和最低分。,写一个函数,求出平均分、最高分和最低分。#include float Max,Min; *全局变量全局变量*void main()() float average(float array ,int n); float ave,score10; int ; for(;);) scanf(,);,); ave= average(,);,); printf(“max=%6.2fnmin=%6.2fn average=%6.2fn“,Ma
46、x,Min,ave); float average(float array ,int n) * 定义函数,形参为数组定义函数,形参为数组 */ int ; float aver,sum=array; Max=Min=array; for(=;);) if(arrayMax)Maxarray; else if(arrayMin)Min array; sum=sum+array; aver; return();); 运行情况如下:运行情况如下: 建议:不必要时不要使用全局变量,原因如下: 全局变量在程序的全部执行过程中都占用存储单元,而不是仅在需要时才开辟单元。 使用全局变量过多,会降低程序的清晰
47、性。在各个函数执行时都可能改变外部变量的值,程序容易出错。因此,要限制使用全局变量。降低函数的通用性。因为函数在执行时要依赖于其所在的外部变量。如果将一个函数移到另一个文件中,还要将有关的外部变量及其值一起移过去。但若该外部变量与其他文件的变量同名时,就会出现问题,降低了程序的可靠性和通用性。一般要求把程序中的函数做成一个封闭体,除了可以通过“实参形参”的渠道与外界发生联系外,没有其他渠道。例例 7.7.6 6 外部变量与局部变量同名外部变量与局部变量同名#include int a=3,b=5; /* a,b为外部变量为外部变量*/ a,b作用范围作用范围void main ( ) int
48、a=8; /*a为局部变量为局部变量 */ 局部变量局部变量a作用范围作用范围 printf (%d, max (a,b); 全局变量全局变量b的作用范围的作用范围 max (int a, int b) /*a,b为局部变量为局部变量 */ int c; c=ab?a b; 形参形参a、b作用范围作用范围 return (c); 运行结果为运行结果为 8 7.7. 变量的存储类别变量的存储类别 7.9.1 7.9.1 动态存储方式与静态存储方式动态存储方式与静态存储方式 从变量的作用域(即从空间)角度来分,可以分为全局变量和局部变量。从变量值存在的时间角度来分,又可以分为静态存储方式和动态存储
49、方式。静态存储方式:指在程序运行期间由系统分配固定的存储空间的方式。动态存储方式:则是在程序运行期间根据需要进行动态的分配存储空间的方式。这个存储空间可以分为三部分:1.1.程序区程序区 2.2.静态存储区静态存储区 3.3.动态存储区动态存储区 变量和函数有两个属性:数据类型数据类型和数据的存储数据的存储类别类别。存储类别指的是数据在内存中存储的方式。存储方式分为两大类:静态存储类和动态存储类。包含:自动的(自动的(auto););静态的(静态的(static););寄存器的(寄存器的(register););外部的(外部的(extern)。 根据变量的存储类别,可以知道变量的作用域和生存期
50、。 7.9.2 auto7.9.2 auto变量变量自动变量auto:不专门声明为static存储类别的局部变量都是动态分配存储空间,在调用该函数时系统会给它们分配存储空间,在函数调用结束时就自动释放这些存储空间。因此这类局部变量称为自动变量。函数中的形参和在函数中定义的变量(包括在复合语句中定义的变量),都属此类。自动变量用关键字auto作存储类别的声明。例如:例如:intint (intint ) * *定义定义f f函数,为形参函数,为形参 * *auto auto intint ,;,;/*/*定义、为自动变量定义、为自动变量 * * 7.9.3 7.9.3 用用staticstati
51、c声明局部变量声明局部变量 当函数中的局部变量的值在函数调用结束后不消失而保留原值时,该变量称为静态局部变量。用关键字static进行声明。例例77 考察静态局部变量的值考察静态局部变量的值#include void main()()int (int); int ,;,; for(; printf( ,();,(); int (int )auto int ; static ; ; return();); 对静态局部变量的说明:对静态局部变量的说明:(1) 静态局部变量属于静态存储类别,在静态存储区内分配存储单元。在程序整个运行期间都不释放。而自动变量(即动态局部变量)属于动态存储类别,占动态存
52、储区空间而不占静态存储区空间,函数调用结束后即释放。(2)对静态局部变量是在编译时赋初值的,即只赋初值一次,在程序运行时它已有初值。以后每次调用函数时不再重新赋初值而只是保留上次函数调用结束时的值。(3)如在定义局部变量时不赋初值的话,则对静态局部变量来说,编译时自动赋初值(对数值型变量)或空字符(对字符变量)。而对自动变量来说,如果不赋初值则它的值是一个不确定的值。(4)虽然静态局部变量在函数调用结束后仍然存在,但其他函数不能引用它。例例78 输出到的阶乘值输出到的阶乘值#include void main()()int fac(int ); int ; for(;);) printf(%!
53、=,fac(););Int fac(int )static int ; *; return();); 7.9.4 register7.9.4 register变量变量 变量的值是存放在内存中的。当程序中用到哪一个变量的值时,由控制器发出指令将内存中该变量的值送到运算器中。 经过运算器进行运算,如果需要存数,再从运算器将数据送到内存存放。 如果有一些变量使用频繁,则为存取变量的值要花费不少时间。为提高执行效率,语言允许将局部变量的值放在CPU中的寄存器中,需要用时直接从寄存器取出参加运算,不必再到内存中去存取。由于对寄存器的存取速度远高于对内存的存取速度,因此这样做可以提高执行效率。这种变量叫做
54、寄存寄存器变量器变量,用关键字registerregister作声明。例例719使用寄存器变量使用寄存器变量#include void main ( )long fac(long); long i,n; scanf(%ld,&n); for(i=1;i=n;i+) printf(%ld!=%ldn,i,fac(i);long fac(long n)register long i,f=1; /*定义寄存器变量定义寄存器变量*/ for (i=1;i=n;i+) f=f*i; return (f);7.9.57.9.5 用用externextern声明外部变量声明外部变量 外部变量是在函数的外部定
55、义的全局变量,它的作用域是从变量的定义处开始,到本程序文件的末尾。在此作用域内,全局变量可以为程序中各个函数所引用。编译时将外部变量分配在静态存储区。用extern来声明外部变量,以扩展外部变量的作用城。1. 在一个文件内声明外部变量在一个文件内声明外部变量例例720 用用extern声明外部变量,扩展它在程声明外部变量,扩展它在程序文件中的作用域序文件中的作用域#include void main() int max(int,int); *外部变量声明外部变量声明* extern A,B; printf(%dn,max(A,B); int A=13,B=-8; *定义外部变量定义外部变量*
56、int max(int x,int y) *定义函数定义函数 * int z; z=xy?x:y; return(z); 2. 在多文件的程序中声明外部变量在多文件的程序中声明外部变量#include int A; /*定义外部变量定义外部变量*/void main() int (int);); /*函数声明函数声明*/ int ,;,; printf(enter the number a and its power m:n);); scanf(,A,);); A*; printf(*,A,);,); ();(); printf(*n,A,);,); 例例7.7. 用用externextern
57、将将外外部部变变量量的的作作用用域域扩扩展展到到其其他他文文件件。本本程程序序的的作作用用是是给给定定的的值值,输输入入和和,求求和和amam的的值。文件值。文件filefile. .中的内容为:中的内容为:文件文件file中的内容为:中的内容为:extern A; /*声明声明A为一个已定义的外部变量为一个已定义的外部变量*/ int powre(int );int ,;,; for(;);) *A; return();); 7.9.67.9.6 用用staticstatic声明外部变量声明外部变量在程序设计中,某些外部变量只限于被本文件引用,而不能被其他文件引用。这时可以在定义外部变量时加
58、一个staitic声明。例如:例如:file1.c file2.cstatic int A; extern int A;void main ( ) void fun (int n) A=A*n; 7.9.7 7.9.7 关于变量的声明和定义关于变量的声明和定义定义性声明定义性声明:需要建立存储空间的需要建立存储空间的(如:如:int a; )声明。声明。引用性声明引用性声明:不需建立存储空间的声明(不需建立存储空间的声明(extern a;)。注意:注意: 声明包括定义,但并非所有的声明都是定义。对声明包括定义,但并非所有的声明都是定义。对“int a;” 而言,它既是声明,又是定义。而对而言
59、,它既是声明,又是定义。而对“extern a;” 而言,它是声明而不是定义。而言,它是声明而不是定义。 7.7.8 .8 存储类别小结存储类别小结(1)从作用域角度分,有局部变量和全局变量。它们采用的存储类别如下:局部变量包括:自动变量、 静态局部变量、寄存器变量。 形式参数可以定义为自动变量或寄存器变量。全局变量包括:静态外部变量、外部变量。(2)从变量存在的时间来区分,有动态存储和静态存储两种类型。静态存储是程序整个运行时间都存在,而动态存储则是在调用函数时临时分配单元。 动态存储 :自动变量、寄存器变量、形式参数。静态存储:态局部变量、静态外部变量 、外部变量。(3) 从变量值存放的位
60、置来区分,可分为:内存中静态存储区:静态局部变量、静态外部变量、 外部变量。内存中动态存储区:自动变量和形式参数。CPU中的寄存器:寄存器变量。(4) static对局部变量和全局变量的作用不同。对局部变量来说,它使变量由动态存储方式改变为静态存储方式。而对全局变量来说,它使变量局部化,但仍为静态存储方式。从作用域角度看,凡有static声明的,其作用域都是局限的,或者是局限于本函数内,或者局限于本文件内。 7.107.10 内部函数和外部函数内部函数和外部函数 根据函数能否被其他源文件调用,将函数区分为内部内部函数函数和外部函数外部函数。7.10.17.10.1内部函数内部函数如果一个函数只
61、能被本文件中其他函数所调用,它称为内部函数。在定义内部函数时,在函数名和函数类型的前面加static。即static 类型标识符 函数名(形参表)例如例如: : static int fun ( int a , int b ) 7.10.2 7.10.2 外部函数外部函数(1) 定义函数时,如果在函数首部的最左端加关键字extern,则表示此函数是外部函数,可供其他文件调用。例如,函数首部可以写为extern int fun (int a, int b),这样,函数fun就可以为其他文件调用。如果在定义函数时省略extern,则隐含为外部函数。(2) 在需要调用此函数的文件中,用extern对
62、函数作声明,表示该函数是在其他文件中定义的外部函数 。例例 7.22 7.22 有一个若干字符的字符串有一个若干字符的字符串, ,今输入一个字符今输入一个字符, ,要要求程序将字符串中该字符删去。用外部函数实现。求程序将字符串中该字符删去。用外部函数实现。File.c(文件)文件)#include void main() extern void enter_string(char str); extern void detele_string(char str,char ch); extern void print_string(char str);*以上以上3行声明在行声明在本函数中将要调用
63、的在其他文件中定义的本函数中将要调用的在其他文件中定义的3个函数个函数* char c; char str80; scanf(%c,&c); detele_string(str,c); print_string(str); file(文件)(文件) #include void enter_string(char str80) * 定义外部函数定义外部函数 enter-string* gets(str); *向字符数组输入字符串向字符数组输入字符串* file(文件)(文件)void delete_string(char str,char ch) *定义外部函数定义外部函数 delete_string * int i,j; for(i=j=0;stri!=0;i+) if(stri!=ch) strj+=stri; stri=0;file(文件)(文件)#include void print_string(char str) printf(%sn,str); 运行情况如下:运行情况如下: (输入)输入) (输入要删去的字符)(输入要删去的字符) (输出已删去指定字符的字符串)(输出已删去指定字符的字符串)