Presentation is loading. Please wait.

Presentation is loading. Please wait.

第八章 函数.

Similar presentations


Presentation on theme: "第八章 函数."— Presentation transcript:

1 第八章 函数

2 本章要点 函数的概念 函数的定义与调用 函数的递归调用 变量的作用域 函数的作用域

3 主要内容 8.1 概述 8.2函数定义的一般形式 8.3函数参数和函数的值 8.4 函数的调用 8.5 函数的嵌套调用 8.6函数的递归调用
8.7数组作为函数参数 8.8 局部变量和全局变量 8.9变量的存储类别 8.10 内部函数和外部函数

4 8.1 概述 一个C程序可由一个主函数和若干个其他函数构成。一个较大的程序可分为若干个程序模块,每一个模块用来实现一个特定的功能。在高级语言中用子程序实现模块的功能。子程序由函数来完成。 函数间的调用关系:由主函数调用其他函数,其他函数也可以互相调用。同一个函数可以被一个或多个函数调用任意多次。

5

6 例8.1先举一个函数调用的简单例子 # include <stdio.h> void main() {
void printstar(); /*对printstar函数声明*/ void print_message(); /*对print_message函数声明*/ printstar(); /*调用printstar函数*/ print_message(); /*调用print_message函数*/ printstar(); /*调用printstar函数*/ }

7 运行情况如下: * * * * * * * * * * * * * * * * How do you do!
void printstar() /*定义printstar函数*/ { printf("* * * * * * * * * * * * * * * *\n"); } void print_message() /*定义print_message函数*/ printf("How do you do!\n"); 运行情况如下: * * * * * * * * * * * * * * * * How do you do!

8 说明: (1)一个C程序由一个或多个程序模块组成,每一个程序模块作为一个源程序文件。对于较大的程序,通常将程序内容分别放在若干个源文件中,再由若干源程序文件组成一个C程序。这样便于分别编写、分别编译,提高调试效率。一个源程序文件可以为多个C程序公用。

9 (2) 一个源程序文件由一个或多个函数以及其他有关内容(如命令行、数据定义等)组成。一个源程序文件是一个编译单位,在程序编译时是以源程序文件为单位进行编译的,而不是以函数为单位进行编译的。
(3) C程序的执行是从main函数开始的,如果在main函数中调用其他函数,在调用后流程返回到main函数,在main函数中结束整个程序的运行。

10 (4)所有函数都是平行的,即在定义函数时是分别进行的,是互相独立的。一个函数并不从属于另一函数,即函数不能嵌套定义。函数间可以互相调用,但不能调用main函数。main函数是系统调用的。

11 (5)从用户使用的角度看,函数有两种: ① 标准函数,即库函数。这是由系统提供的,用户不必自己定义这些函数,可以直接使用它们。不同的C系统提供的库函数的数量和功能会有一些不同,但许多基本的函数是共同的。 ② 用户自己定义的函数。用以解决用户的专门需要。

12 (6) 从函数的形式看,函数分两类: ①无参函数。无参函数一般用来执行指定的一组操作。在调用无参函数时,主调函数不向被调用函数传递数据。 ②有参函数。主调函数在调用被调用函数时,通过参数向被调用函数传递数据。

13 8.2函数定义的一般形式 8.2.1 无参函数的定义一般形式 定义无参函数的一般形式为: 类型标识符 函数名() 声明部分 语句部分

14 8.2.2 有参函数定义的一般形式 定义有参函数的一般形式为: 类型标识符 函数名(形式参数表列) { 声明部分 语句部分 } 例如:
类型标识符 函数名(形式参数表列) 声明部分 语句部分 例如: int max(int x,int y)  {int z;/ *函数体中的声明部分*/   z=x>y?x∶y; return(z);

15 8.2.3 空函数 定义空函数的一般形式为: 类型标识符 函数名() { } 例如: dummy()
类型标识符 函数名() { } 例如: dummy() 主调函数调用空函数时,只表明这里要调用一个函数,但函数本身什么工作也不做等,以后扩充函数功能时补充上。

16 8.3函数参数和函数的值 8.3.1形式参数和实际参数 形式参数:函数名后面括号中的变量名称为“形式参数”(简称“形参”)。
实际参数:主调函数中调用一个函数时,函数名后面括号中的参数(可以是一个表达式)称为“实际参数”(简称“实参”)。 函数返回值:return后面的括号中的值作为函数带回的值(称函数返回值)。

17 主调函数和被调用函数之间有数据传递的关系。在不同的函数之间传递数据,可以使用的方法有:
参数:通过形式参数和实际参数 返回值:用return语句返回计算结果 全局变量:外部变量

18 例8.2调用函数时的数据传递 #include <stdio.h> void main() { int max(int x,int y); /* 对max函数的声明 */ int a,b,c; scanf("%d,%d",&a,&b); c=max(a,b); printf("Max is %d",c);

19 7,8↙ Max is 8 运行情况如下: int max(int x,int y)/*定义有参函数max */ { int z;
z=x>y?x∶y; return(z); 运行情况如下: 7,8↙ Max is 8

20 通过函数调用,可使两个函数中的数据发生联系。

21 关于形参与实参的说明: (1) 在定义函数中指定的形参,在未出现函数调用时,它们并不占内存中的存储单元。只有在发生函数调用时,函数max中的形参才被分配内存单元。在调用结束后,形参所占的内存单元也被释放。 (2) 实参可以是常量、变量或表达式, 例如: max(3,a+b); 但要求它们有确定的值。在调用时将实参的值赋给形参。

22 (3)在被定义的函数中,必须指定形参的类型。
(4)实参与形参的类型应相同或赋值兼容。 (5)值传递:实参向形参的数据传递是单向“值传递”,只能由实参传给形参,而不能由形参传回来给实参。 在调用函数时,给形参分配存储单元,并将实参对应的值传递给形参,调用结束后,形参单元被释放,实参单元仍保留并维持原值。

23 8.3.2 函数的返回值 函数的返回值是通过函数调用使主调函数得到的确定值。
例如:例8.2中,max(2,3)的值是3,max(5,2)的值是5。赋值语句将这个函数值赋给变量c。

24 说明: (1)函数的返回值是通过函数中的return语句获得的。
一个函数中可以有一个以上的return语句,执行到哪一个return语句,哪一个语句起作用。 return语句后面的括弧也可以不要 例如: “return z;” 等价于 “return (z);” return后面的值可以是一个表达式。 例如: max(int x,int y) {  return(x>y?x:y); }

25 注意: 凡不加类型说明的函数,自动按整型处理。 (2)函数的返回值应当属于某一个确定的类型,在定义函数时指定函数返回值的类型。
例如:下面是3个函数的首行: int max(float x,float y) /* 函数值为整型 */ char letter(char c1,char c2) /* 函数值为字符型 */ double min(int x,int y) /* 函数值为双精度型 */ 注意: 凡不加类型说明的函数,自动按整型处理。

26 (3)在定义函数时指定的函数类型一般应该和return语句中的表达式类型一致。
对数值型数据,可以自动进行类型转换。即函数类型决定返回值的类型。 (4)对于不带回值的函数,应当用“void”定义函数为“无类型”(或称“空类型”)。此时在函数体中不得出现return语句。

27 运行情况如下: 1.5, 2.5↙ Max is 2 例 8.3 返回值类型与函数类型不同
# include <stdio.h> void main() { int max(float x,float y);   float a,b;   int c;   scanf("%f,%f,",&a,&b);   c=max(a,b);   printf("Max is %d\n",c); int max(float x,float y) { float z; /* z为实型变量 */ z=x>y?x∶y; return(z); 运行情况如下: 1.5, 2.5↙ Max is 2 

28 8.4 函数的调用 8.4.1 函数调用的一般形式 函数调用的一般形式为: 函数名(实参表列) 说明:
(1)如果是调用无参函数,则“实参表列”可以没有,但括弧不能省略。

29 (2)如果实参表列包含多个实参,则各参数间用逗号隔开。实参与形参的个数应相等,类型应匹配。实参与形参按顺序对应,一一传递数据。
(3)如果实参表列包括多个实参,对实参求值的顺序并不是确定的,有的系统按自左至右顺序求实参的值,有的系统则按自右至左顺序。

30 例 8.4 实参求值的顺序 #include <stdio.h> void main() { int f(int a,int b); /* 函数声明 */ int i=2,p; p=f(i,++i); /* 函数调用 */ printf("%d\n",p); }  

31 int f(int a,int b) /* 函数定义 */
{ int c; if(a>b) c=1; else if(a==b) c=0; else c=-1; return(c); }    

32 int i=2,p; p=f(i,++i); 对于函数调用 如果按自左至右顺序求实参的值,则函数调用相当于f(2,3)
如果按自左至右顺序求实参的值,则函数调用相当于f(3,3)

33 8.4.2 函数调用的方式 按函数在程序中出现的位置来分,可以有以下三种函数调用方式: 1.函数语句
把函数调用作为一个语句。这时不要求函数带回值,只要求函数完成一定的操作。 2.函数表达式 函数出现在一个表达式中,这种表达式称为函数表达式。这时要求函数带回一个确定的值以参加表达式的运算。例如:c=2*max(a,b);

34 3.函数参数 函数调用作为一个函数的实参。 例如: m = max (a , max ( b , c ) ) ; 其中max ( b , c )是一次函数调用,它的值作为max另一次调用的实参。m的值是a、b、c三者中的最大者。

35 在一个函数中调用另一函数(即被调用函数)需要具备哪些条件呢 ?
8.4.3 对被调用函数的声明和函数原型 在一个函数中调用另一函数(即被调用函数)需要具备哪些条件呢 ? 1.首先被调用的函数必须是已经存在的函数(是库函数或用户自己定义的函数)。但光有这一条件还不够。

36 2.如果使用库函数,还应该在本文件开头用#include 命令将调用有关库函数时所需用到的信息“包含”到本文件中来。
3.如果使用用户自己定义的函数,而该函数的位置在调用它的函数(即主调函数)的后面,应该在主调函数中对被调用的函数作声明。

37 函数原型的一般形式为: 1. 函数类型 函数名(参数类型1,参数类型2……); 2. 函数类型 函数名(参数类型1,参数名1,参数类型2,参数名2……); 声明的作用是把函数名、函数参数的个数和参数类型等信息通知编译系统,以便在遇到函数调用时,编译系统能正确识别函数并检查调用是否合法。

38 注意: 函数的“定义”和“声明”的区别: 函数的定义是指对函数功能的确立,包括指定函数名,函数值类型、形参及其类型、函数体等,它是一个完整的、独立的函数单位。 函数的声明的作用则是把函数的名字、函数类型以及形参的类型、个数和顺序通知编译系统,以便在调用该函数时系统按此进行对照检查。

39 例8.5 对被调用的函数作声明 # include <stdio.h> void main() { float add(float x, float y); /*对被调用函数add的声明*/ float a,b,c; scanf("%f,%f",&a,&b); c=add(a,b); printf("sum is %f \n",c); float add(float x,float y) /*函数首部*/ { float z; /* 函数体 */ z=x+y; return(z);

40 例8.5 对被调用的函数作声明 # include <stdio.h> float add(float x,float y) /*函数首部*/ { float z; /* 函数体 */ z=x+y; return(z); void main() float a,b,c; scanf("%f,%f",&a,&b); c=add(a,b); printf("sum is %f \n",c);

41 8.5 函数的嵌套调用 嵌套定义就是在定义一个函数时,其函数体内又包含另一个函数的完整定义 。

42 例 8.6 用弦截法求方程 f(x)=x3-5x2+16x-80=0 的根

43 方法: 1. 取两个不同点x1,x2,如果f(x1)和f(x2)符号相反,则(x1,x2)区间内必有一个根。如果f(x1)与f(x2)同符号,则应改变x1,x2,直到f(x1)、f(x2)异号为止。注意x1、x2的值不应差太大,以保证(x1,x2)区间内只有一个根。 2. 连接(x1,f(x1))和(x2,f(x2))两点,此线(即弦)交x轴于x。

44 3. 若f(x)与f(x1)同符号,则根必在(x,x2)区间内,此时将x作为新的x1。如果f(x)与f(x2)同符号,则表示根在(x1,x)区间内,将x作为新的x2。

45 N-S流程图

46 实现各部分功能的几个函数: 1. 用函数f(x)代表x的函数:x3-5x2+16x-80。
2. 用函数调用xpoint (x1,x2)来求(x1,f(x1))和 (x2,f(x2))的连线与x轴的交点x的坐标。 3. 用函数调用root (x1,x2)来求(x1,x2)区间的 那个实根。显然,执行root函数过程中要用到函 数xpoint,而执行xpoint函数过程中要用 到f函数。

47 #include <stdio.h>
#include <math.h> float f(float x) /* 定义f函数,以实现f(x) =x3-5x2+16x-80 */ float y; y=((x-5.0)*x+16.0)*x-80.0; return(y);

48 float xpoint (float x1,float x2)
/*定义xpoint函数,求出弦与x轴交点 */{ float y; y=(x1*f(x2)-x2*f(x1)) /f(x2)-f(x1)); return (y);

49 float root(float x1,float x2)
y1=f(x1); do { x=xpoint(x1,x2); y=f(x);  if(y*y1>0) /*f(x)与f(x1)同符号 */   { y1=y;   x1=x;}   else x2=x; } while(fabs(y)>=0.0001); return(x

50 运行情况如下: input x1,x2: 2,6 void main() /*主函数 */ {float x1,x2,f1,f2,x;
do {printf("input x1,x2:\n"); scanf("%f,%f",&x1,&x2); f1=f(x1); f2=f(x2); }while(f1*f2>=0); x=root(x1,x2); printf("A root of equation is %8.4f\n",x); 运行情况如下: input x1,x2: 2,6 A root of equation is

51 8.6 函数的递归调用 在调用一个函数的过程中又出现直接或间接地调用该函数本身,称为函数的递归调用。C语言的特点之一就在于允许函数的递归调用。 例如: int f ( int x) { int y,z; z=f(y); return(2*z);

52

53 例 8.7: 有5个人坐在一起,问第5个人多少岁?他说比第4个人大2岁。问第4个人岁数,他说比第3个人大2岁。问第3个人,又说比第2个人大2岁。问第2个人,说比第1个人大2岁。最后问第1个人,他说是10岁。请问第5个人多大。 age(5)= age (4)+2 age(4)= age (3)+2 age(3)= age (2)+2 age(2)= age (1)+2 age(1)= 10 用数学公式表述如下: age(n)= 10 (n=1) age(n-1)+2 (n>1)

54 18 可以用一个函数来描述上述递归过程: int age(int n) /*求年龄的递归函数*/
if(n==1) c=10; else c=age(n-1)+2; return(c); 运行结果如下: 18 用一个主函数调用age函数,求得第5人的年龄。 #include <stdio.h> void main() { printf(″%d″,age(5));

55 例8.8用递归方法求n! 求n!也可以用递归方法,即5!等于4!×5,而4!=3!×4…1!=1。 可用下面的递归公式表示:
n!=1 (n=0,1) n·(n-1)! (n>1)

56 例8.9 Hanoi(汉诺塔)问题:

57 由上面的分析可知:将n个盘子从A座移到C座可以分解为以下3个步骤:
1.将A上n-1个盘借助C座先移到B座上。 2.把A座上剩下的一个盘移到C座上。 3.将n-1个盘从B座借助于A座移到C座上。

58 程序如下: #include <stdio.h> void 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'); }

59 void hanoi(int n,char one,char two,char three)
/* 定义hanoi函数,将n个盘从one座借助two座,移到three座 */ { 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-->%c\n",x,y);

60 运行情况如下: input the number of diskes:3↙ The steps to noving 3 diskes: A-->C A-->B C-->B B-->A B-->C

61 8.7数组作为函数参数 8.7.1 数组元素作函数实参 由于实参可以是表达式,而数组元素可以是表达式的组成部分,因此数组元素可以作为函数的实参,与用变量作实参一样,是单向传递,即“值传送”方式。

62 例 8.10 有两个数组a和b,各有10个元素,将它们对应地逐个相比(即a[0]与b[0]比,a[1]与b[1]比……)。如果a数组中的元素大于b数组中的相应元素的数目多于b数组中元素大于a数组中相应元素的数目(例如,a[i]>b[i]6次,b[i]>a[i]3次,其中i每次为不同的值),则认为a数组大于b数组,并分别统计出两个数组相应元素大于、等于、小于的次数。

63 #include <stdio.h>
void main() { int large(int x,int y); /* 函数声明 */ int a[10],b[10],i,n=0,m=0,k=0; printf(″enter array a∶\n″); for(i=0;i<10;i++=) scanf(″%d″,&a[i]); printf(″\n″); printf(″ enter arrayb∶\n″); scanf (″%d″,&b[i]); for(i=0;i<10;i++) { if(large (a[i],b[i] )== 1) n=n+1;   else if( large (a[i],b[i] )==0) m=m+1; else k=k+1;}

64 printf("a[i]>b[i] %d times\na[i]=b[i] %d
times\na[i]<b[i] %d times\n",n,m,k); if(n>k) printf("array a is larger than array b\n"); else if (n<k) printf("array a is smaller than array b\n");  else printf("array a is equal to array b\n"); large(int x,int y) { int flag; if(x>y)flag=1;  else if(x<y)flag=-1;   else flag=0; return(flag);

65 运行情况如下: enter array a: 1 3 5 7 9 8 6 4 2 0↙ enter array b∶ –1 – ↙ a[i]>b[i] 4 times a[i]=b[i] 1 times a[i]<b[i] 5 times array a is smaller thann array b

66 8.7.2 数组名作函数参数 例8.11 用数组名作函数参数时,此时形参应当用数组名或用指针变量 。
有一个一维数组score,内放10个学生成绩,求平均成绩。

67 #include <stdio.h>
void main() { float average(float array[10]); /* 函数声明 */ float score[10] , aver;   int i;   printf(″input 10 scores:\n″);   for(i=0;i<10;i++= scanf(″%f″,&score[i]);   printf(″\n″);   aver=average( score );   printf (″ average score is %5.2f\n″, aver); } 

68 float average (float array[10]) { int i; float aver,sum=array[0];
  for (i=1;i<10;i++=)  sum=sum+array[i];  aver=sum/10;  return(aver); 运行情况如下: input 10 scores: 100 56 78 98.5 76 87 99 67.5 75 97↙ average score is 83.40

69 例 8.12形参数组不定义长度 #include <stdio.h> void main() { float average(float array[ ],int n) float score_1[5] ={98.5,97,9 1.5,60,55}; float score_2[10]={ 67.5,89.5,99,69.5, 77,89.5,76.5,54,60,99.5};   printf(“the average of class A is %6.2f\n”, average(score_1,5)); printf(“the average of class B is %6.2f\n”, average(score_2,10));

70 float average(float array[ ],int n)
  for(i=1;i<n;i++= sum=sum+array[i];   aver=sum/n;   return(aver); 运行结果如下: the average of class A is 80.40 The average of class B is

71 例 8.13 用选择法对数组中10个整数按由小到大排序。
所谓选择法就是先将10个数中最小的数与a[0]对换;再将a[1]到a[9]中最小的数与a[1]对换……每比较一轮,找出一个未经排序的数中最小的一个。共比较9轮。

72 未排序时的情况: a[0] a[1] a[2] a[3] a[4] 将5个数中最小的数1与a[0]对换: 将余下的4个数中最小的数3与a[1]对换 将余下的3个数中最小的数4与a[2]对换 将余下的2个数中最小的数6与a[3]对换

73 程序: #include <stdio.h> void main() { void sort(int array[],int n); int a[10],i; printf(″enter the array\n″); for(i=0;i<10;i++=   scanf(″%d″,&a[i]); sort(a,10); printf(″the sorted array∶\n″);    printf(″%d″,a[i]);   printf(″\n″);   } 

74 void sort(int array[],int n)/*排序函数*/
for(i=0;i<n-1;i++)   { k=i;      for(j=i+1;j<n;j++)    if(array[j] < array[k]=k=j;      t=array[k]; array[k]=array[i];array[i]=t

75 #include <stdio.h> void main() { max_value ( int array[ ][4]);
多维数组名作函数参数 用多维数组名作为函数实参和形参。在被调函数中对形参数组定义时可以指定每一维的大小 。 程序: #include <stdio.h> void main() { max_value ( int array[ ][4]); int [3][4]={{1,3,5,7},{2,4,6,8},{15,17,34,12}}; printf(″max value is %d\n″, max_value(a) ); }  

76 Max value is 34 max_value ( int array[ ][4]) { int i,j,k,max;
  for(i=0;i<3;i++)  for(j=0;j<4;j++=    if(array[i][j]>max) max= array [i][j]; return(max); }    运行结果如下: Max value is 34

77 8.8局部变量和全局变量 8.8.1局部变量 内部变量:在一个函数内部定义的变量称内部变量。它只在本函数范围内有效,即:只有在本函数内才能使用这些变量,故称为“局部变量” 。

78 例: float f1( int a) /*函数f1 */ {int b,c; … /* a、b、c有效*/ } char f2(int x,int y) /*函数f2 */ {int i,j; /* x、y、i、j有效*/ void main( ) /*主函数*/ {int m,n; … /* m、n有效*/ }   

79 主函数中定义的变量只在主函数中有效,而不因为在主函数中定义而在整个文件或程序中有效。主函数也不能使用其他函数中定义的变量。
(2) 不同函数中可以使用相同名字的变量,它们代表不同的对象,互不干扰。 (3) 形式参数也是局部变量。 (4) 在一个函数内部,可以在复合语句中定义变量,这些变量只在本复合语句中有效,这种复合语句也称为“分程序”或“程序块”。

80 void main ( ) {int a,b; {int c; c=a+b; c在此范围内有效 a,b在此范围内有效 } }  

81 8.8.2 全局变量 外部变量:函数之外定义的变量称为外部变量。外部变量可以为本文件中其他函数所共用。它的有效范围为从定义变量的位置开始到本源文件结束。所以也称全程变量。

82 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 ( ) /*主函数*/ {int m,n; }   

83 例8.15 有一个一维数组,内放10个学生成绩 ,写一个函数,求出平均分、最高分和最低分。 #include <stdio.h> float Max=0,Min=0; /*全局变量*/ void main() { float average(float array[ ],int n); float ave,score[10]; int i;   for(i=0;i<10;i++)   scanf(″%f″,&score[i]); ave= average(score,10);   printf(“max=%6.2f\nmin=%6.2f\n average=%6.2f\n“,Max,Min,ave); }

84 float average(float array[ ],int n)
/* 定义函数,形参为数组 */ { int i; float aver,sum=array[0]; Max=Min=array[0]; for(i=1;i<n;i++) { if(array[i]>Max)Max=array[i];   else if(array[i]<Min)Min= array[i];  sum=sum+array[i];   } aver=sum/n; return(aver); 运行情况如下: 99 45 78 97 100 67.5 89 92 66 43↙ max=100.00 min=43.00 average=77.65

85

86 建议:不必要时不要使用全局变量,原因如下:
① 全局变量在程序的全部执行过程中都占用存储单元,而不是仅在需要时才开辟单元。 ② 使用全局变量过多,会降低程序的清晰性。在各个函数执行时都可能改变外部变量的值,程序容易出错。因此,要限制使用全局变量。

87 ③降低函数的通用性。因为函数在执行时要依赖于其所在的外部变量。如果将一个函数移到另一个文件中,还要将有关的外部变量及其值一起移过去。但若该外部变量与其他文件的变量同名时,就会出现问题,降低了程序的可靠性和通用性。一般要求把C程序中的函数做成一个封闭体,除了可以通过“实参——形参”的渠道与外界发生联系外,没有其他渠道。

88 例 8.16 外部变量与局部变量同名 #include <stdio.h> int a=3,b=5; /* a,b为外部变量*/ a,b作用范围 void main ( ) { int a=8; /*a为局部变量 */ 局部变量a作用范围 printf (″%d″, max (a,b)); 全局变量b的作用范围 } max (int a, int b) /*a,b为局部变量 */ { int c; c=a>b?a∶b; 形参a、b作用范围 return (c); 运行结果为 8

89 8.9 变量的存储类别 8.9.1 动态存储方式与静态存储方式 从变量的作用域(即从空间)角度来分,可以分为全局变量和局部变量。
从变量值存在的时间角度来分,又可以分为静态存储方式和动态存储方式。 静态存储方式:指在程序运行期间由系统分配固定的存储空间的方式。 动态存储方式:则是在程序运行期间根据需要进行动态的分配存储空间的方式。这个存储空间可以分为三部分:1.程序区 2.静态存储区 3.动态存储区

90 变量和函数有两个属性:数据类型和数据的存储类别。存储类别指的是数据在内存中存储的方式。
存储方式分为两大类:静态存储类和动态存储类。包含: 自动的(auto); 静态的(static); 寄存器的(register); 外部的(extern)。 根据变量的存储类别,可以知道变量的作用域和生存期。

91 8.9.2 auto变量 自动变量auto:不专门声明为static存储类别的局部变量都是动态分配存储空间,在调用该函数时系统会给它们分配存储空间,在函数调用结束时就自动释放这些存储空间。因此这类局部变量称为自动变量。 函数中的形参和在函数中定义的变量(包括在复合语句中定义的变量),都属此类。 自动变量用关键字auto作存储类别的声明。 例如: int f(int a) /*定义f函数,a为形参 */ {auto int b,c=3;/*定义b、c为自动变量 */  … }

92 用static声明局部变量 当函数中的局部变量的值在函数调用结束后不消失而保留原值时,该变量称为静态局部变量。用关键字static进行声明。

93 例8.17 考察静态局部变量的值 #include <stdio.h> void main() {int f(int); int a=2,i; for(i=0;i<3;i++= printf(″%d ″,f(a));  } int f(int a) {auto int b=0; static c=3; b=b+1;  c=c+1; return(a+b+c);   }

94 对静态局部变量的说明: (1) 静态局部变量属于静态存储类别,在静态存储区内分配存储单元。在程序整个运行期间都不释放。而自动变量(即动态局部变量)属于动态存储类别,占动态存储区空间而不占静态存储区空间,函数调用结束后即释放。 (2)对静态局部变量是在编译时赋初值的,即只赋初值一次,在程序运行时它已有初值。以后每次调用函数时不再重新赋初值而只是保留上次函数调用结束时的值。

95 (3)如在定义局部变量时不赋初值的话,则对静态局部变量来说,编译时自动赋初值0(对数值型变量)或空字符(对字符变量)。而对自动变量来说,如果不赋初值则它的值是一个不确定的值。
(4)虽然静态局部变量在函数调用结束后仍然存在,但其他函数不能引用它。

96 例8.18 输出1到5的阶乘值 #include <stdio.h> void main() {int fac(int n); int i; for(i=1;i<=5;i++) printf(″%d!=%d\n″,i,fac(i)); Int fac(int n) {static int f=1; f=f*n; return(f);

97 8.9.4 register变量 变量的值是存放在内存中的。当程序中用到哪一个变量的值时,由控制器发出指令将内存中该变量的值送到运算器中。 经过运算器进行运算,如果需要存数,再从运算器将数据送到内存存放。

98 如果有一些变量使用频繁,则为存取变量的值要花费不少时间。为提高执行效率,C语言允许将局部变量的值放在CPU中的寄存器中,需要用时直接从寄存器取出参加运算,不必再到内存中去存取。由于对寄存器的存取速度远高于对内存的存取速度,因此这样做可以提高执行效率。这种变量叫做寄存器变量,用关键字register作声明。

99 例8.19使用寄存器变量 #include <stdio.h> void main ( ) {long fac(long); long i,n; scanf("%ld",&n); for(i=1;i<=n;i++) printf("%ld!=%ld\n",i,fac(i)); } long fac(long n) {register long i,f=1; /*定义寄存器变量*/ for (i=1;i<=n;i++) f=f*i; return (f);

100 8.9.5 用extern声明外部变量 外部变量是在函数的外部定义的全局变量,它的作用域是从变量的定义处开始,到本程序文件的末尾。在此作用域内,全局变量可以为程序中各个函数所引用。编译时将外部变量分配在静态存储区。用extern来声明外部变量,以扩展外部变量的作用城。

101 1. 在一个文件内声明外部变量 例8.20 用extern声明外部变量,扩展它在程序文件中的作用域 #include <stdio.h> void main() { int max(int,int); /*外部变量声明*/ extern A,B; printf("%d\n",max(A,B)); } int A=13,B=-8; /*定义外部变量*/ int max(int x,int y) /*定义max函数 */ { int z; z=x>y?x:y; return(z);

102 2. 在多文件的程序中声明外部变量 例8.21 用extern将外部变量的作用域扩展到其他文件。本程序的作用是给定b的值,输入a和m,求a×b和am的值。文件file1.c中的内容为: #include <stdio.h> int A; /*定义外部变量*/ void main() {int power(int); /*函数声明*/ int b=3,c,d,m; printf(″enter the number a and its power m:\n″); 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);

103 文件file2.c中的内容为: extern A; /*声明A为一个已定义的外部变量*/ int powre(int n); {int i,y=1; for(i=1;i<=n;i++) y*=A; return(y);

104 用static声明外部变量 在程序设计中,某些外部变量只限于被本文件引用,而不能被其他文件引用。这时可以在定义外部变量时加一个staitic声明。 例如: file1.c file2.c static int A; extern int A; void main ( ) void fun (int n) { {… … A=A*n; }

105 8.9.7 关于变量的声明和定义 定义性声明:需要建立存储空间的(如:int a; )声明。
关于变量的声明和定义 定义性声明:需要建立存储空间的(如:int a; )声明。 引用性声明:不需建立存储空间的声明(extern a;)。 注意: 声明包括定义,但并非所有的声明都是定义。对“int a;” 而言,它既是声明,又是定义。而对“extern a;” 而言,它是声明而不是定义。

106 8.9.8 存储类别小结 从作用域角度分,有局部变量和全局变量。它们采用的存储类别如下: 局部变量包括:
自动变量、 静态局部变量、寄存器变量。 形式参数可以定义为自动变量或寄存器变量。 全局变量包括: 静态外部变量、外部变量。

107 (2)从变量存在的时间来区分,有动态存储和静态存储两种类型。静态存储是程序整个运行时间都存在,而动态存储则是在调用函数时临时分配单元。
动态存储 :自动变量、寄存器变量、形式参数。 静态存储:态局部变量、静态外部变量 、外部变量。

108 (3) 从变量值存放的位置来区分,可分为: 内存中静态存储区:静态局部变量、静态外部变量、 外部变量。 内存中动态存储区:自动变量和形式参数。 CPU中的寄存器:寄存器变量。

109 (4) static对局部变量和全局变量的作用不同。对局部变量来说,它使变量由动态存储方式改变为静态存储方式。而对全局变量来说,它使变量局部化,但仍为静态存储方式。从作用域角度看,凡有static声明的,其作用域都是局限的,或者是局限于本函数内,或者局限于本文件内。

110 8.10 内部函数和外部函数 8.10.1内部函数 根据函数能否被其他源文件调用,将函数区分为内部函数和外部函数。
如果一个函数只能被本文件中其他函数所调用,它称为内部函数。在定义内部函数时,在函数名和函数类型的前面加static。即 static 类型标识符 函数名(形参表) 例如: static int fun ( int a , int b )

111 外部函数 (1) 定义函数时,如果在函数首部的最左端加关键字extern,则表示此函数是外部函数,可供其他文件调用。例如,函数首部可以写为extern int fun (int a, int b),这样,函数fun就可以为其他文件调用。如果在定义函数时省略extern,则隐含为外部函数。 (2) 在需要调用此函数的文件中,用extern对函数作声明,表示该函数是在其他文件中定义的外部函数 。

112 例 8.22 有一个若干字符的字符串,今输入一个字符,要求程序将字符串中该字符删去。用外部函数实现。
File.c(文件1) #include <stdio.h> void main() { extern void enter_string(char str[]); extern void detele_string(char str[],char ch); extern void print_string(char str[]);/*以上3行声明在本函数中将要调用的在其他文件中定义的3个函数*/ char c; char str[80]; scanf("%c",&c); detele_string(str,c); print_string(str); }

113 file2.c(文件2) #include <stdio.h> void enter_string(char str[80]) /* 定义外部函数 enter-string*/ { gets(str); /*向字符数组输入字符串*/ }  file3.c(文件3) void delete_string(char str[],char ch) /*定义外部函数 delete_string */ { int i,j; for(i=j=0;str[i]!='\0';i++) if(str[i]!=ch) str[j++]=str[i]; str[i]='\0'; }

114 #include <stdio.h> void print_string(char str[]) {
file4.c(文件4) #include <stdio.h> void print_string(char str[]) { printf("%s\n",str); }  运行情况如下: abcdefgc↙ (输入str)   c↙  (输入要删去的字符) abdefg (输出已删去指定字符的字符串)


Download ppt "第八章 函数."

Similar presentations


Ads by Google