Download presentation
Presentation is loading. Please wait.
Published byHandoko Dharmawijaya Modified 6年之前
1
第5章 函数及变量的存储类别 5.0 概述 5.1函数的定义 5.2函数调用 5.3变量的作用域和存储类别 5.4内部函数和外部函数
第5章 函数及变量的存储类别 5.0 概述 5.1函数的定义 5.2函数调用 5.3变量的作用域和存储类别 5.4内部函数和外部函数 5.5函数的嵌套调用和递归调用 使每一个模块成为相对独立、功能单一、结构清晰、接口简单、容易理解的程序 每个模块可以独立设计算法,单独编写和测试 一个模块中的错误不易扩散和蔓延到其它模块, 众人可同时进行集体性开发 软件具有模块结构,软件开发工作如同搭积木,一个模块可以在不同程序中多次使用
2
第5章 函数及变量的存储类别 5.0 概述 模块化程序设计 基本思想:将一个大的程序按功能分割成一些小模块, 特点:
第5章 函数及变量的存储类别 5.0 概述 模块化程序设计 基本思想:将一个大的程序按功能分割成一些小模块, 特点: 各模块相对独立、功能单一、结构清晰、接口简单 控制了程序设计的复杂性 提高元件的可靠性 缩短开发周期 避免程序开发的重复劳动 易于维护和功能扩充 开发方法: 自上向下,逐步分解,分而治之 使每一个模块成为相对独立、功能单一、结构清晰、接口简单、容易理解的程序 每个模块可以独立设计算法,单独编写和测试 一个模块中的错误不易扩散和蔓延到其它模块, 众人可同时进行集体性开发 软件具有模块结构,软件开发工作如同搭积木,一个模块可以在不同程序中多次使用
3
C是模块化程序设计语言 C程序结构 C是函数式语言 必须有且只能有一个名为main的主函数
C程序的执行总是从main函数开始,在main中结束 函数不能嵌套定义,可以嵌套调用
4
函数分类 从用户角度 从函数形式 使用库函数应注意: 1、函数功能 2、函数参数的数目和顺序,及各参数意义和类型 3、函数返回值意义和类型
标准函数(库函数):由系统提供 用户自定义函数 从函数形式 无参函数 有参函数 使用库函数应注意: 1、函数功能 2、函数参数的数目和顺序,及各参数意义和类型 3、函数返回值意义和类型 4、需要使用的包含文件
5
C(n,k)= ──── k!(n-k)! 【例5.2】编写程序计算组合数: #include"stdio.h" main()
{int n,k,c; printf("\nplease input( n,k): "); /*输入n、k的值*/ scanf("%d,%d",&n,&k); c=fac(n)/(fac(k)*fac(n-k)); /*三次调用函数fac,求n!、k!、(n-k)!*/ printf("\nC(n,k)=%d",c); /*输出计算结果*/ } int fac(int m) /*计算一个整数的阶乘*/ {int i,s=1; for(i=1;i<=m;i++) s*=i; return(s);
6
5.1 函数的定义 5.1.1 一般格式 现代风格: 函数返回值类型 缺省int型 无返回值void 合法标识符
函数类型 函数名(形参类型说明表) { 说明部分 语句部分 } 现代风格: 函数体 例 有参函数(现代风格) int max(int x, y) { int z; z=x>y?x:y; return(z); } 例 有参函数(现代风格) int max(int x,int y) { int z; z=x>y?x:y; return(z); } 例 无参函数 printstar( ) { printf(“**********\n”); } 或 printstar(void ) 例 空函数 dummy( ) { } 函数体为空
7
传统风格: 函数类型 函数名(形参表) 形参类型说明 { 说明部分 语句部分 } 例 有参函数(传统风格) int max(x,y)
函数类型 函数名(形参表) 形参类型说明 { 说明部分 语句部分 } 传统风格: 例 有参函数(传统风格) int max(x,y) int x,y; { int z; z=x>y?x:y; return(z); }
8
【例5.1】编写函数,求三个整型参数的最大值。 int max(int x1,int x2,int x3) /*定义函数的返回值类型,函数名,形参*/ {int max; if (x1>x2) max=x1; else max=x2; if (max<x3) max=x3; /*通过比较得到三个数的最大值放到max中*/ return(max); /*返回运算结果*/ }
9
5.1.2 函数的返回值 返回语句 例 无返回值函数 形式: return(表达式); void swap(int x,int y )
功能:使程序控制从被调用函数返回到调用函数中,同时把返值带给调用函数 说明: 函数中可有多个return语句 若无return语句,遇到函数结束的“}”时,自动返回调用函数 若函数类型与return语句中表达式值的类型不一致,按前者为准,自动转换------函数调用转换 void型函数:无返回值的函数 例 无返回值函数 void swap(int x,int y ) { int temp; temp=x; x=y; y=temp; }
10
{ printf("**********"); } main() { int a; a=printstar();
例 无返回值的函数返回后带回不确定值 printstar() { printf("**********"); } main() { int a; a=printstar(); printf("%d",a); void printstar() { printf("**********"); } main() { int a; a=printstar(); printf("%d",a); 输出:10 编译错误!
11
main() { float a,b; int c; scanf("%f,%f",&a,&b); c=max(a,b);
例 函数返回值类型转换 main() { float a,b; int c; scanf("%f,%f",&a,&b); c=max(a,b); printf("Max is %d\n",c); } max(float x, float y) { float z; z=x>y?x:y; return(z);
12
5.2 函数的调用 5.2.1 调用形式: 函数名(实参表) 函数语句:函数调用可单独成为一个语句 例 output(); ;
5.2.1 调用形式: 函数名(实参表) 函数语句:函数调用可单独成为一个语句 例 output(); ; printf(“Hello,World!\n”); 函数表达式: 调用函数后的返回值可参加表达式的计算,这时要求被调函数带返回值。 例 sum=add(a,b); c=fac(n)/(fac(k)*fac(n-k)) ; 函数参数:带返回值的函数调用亦可作为其它函数的参数(实际参数) 例 printf(“%d”,max(a,b)); m=max(a,max(b,c));
13
调用方式 函数语句:函数调用可单独成为一个语句 例 output(); ; printf(“Hello,World!\n”);
函数表达式: 调用函数后的返回值可参加表达式的计算,这时要求被调函数带返回值。 例 sum=add(a,b); c=fac(n)/(fac(k)*fac(n-k)) ; 函数参数:带返回值的函数调用亦可作为其它函数的参数(实际参数) 例 printf(“%d”,max(a,b)); m=max(a,max(b,c));
14
调用函数的过程是: ① 为函数的所有形参分配内存单元。 ②计算各个实参表达式的值,一一对应的赋值给相应形参(若是无参函数,上述过程不执行)。 ③ 进入函数体,执行函数中的语句,实现函数的功能。 ④执行到return语句时,计算return语句中表达式的值(若是无返回值函数,本项不做),返回到主调函数。 ⑤ 释放形参及本函数内的局部变量所占内存, 继续执行主调函数中的后继语句。 说明: 实参与形参个数相等,类型一致,按顺序一一对应 形参与实参的结合顺序,因系统而定(Turbo C 自右向左)
15
例 参数求值顺序 main() { int i=2,p; p=f(i,++i); printf("%d",p); } int f(int a, int b) { int c; if(a>b) c=1; else if(a==b) c=0; else c=-1; return(c); main() { int i=2,p; p=f(i, i++); printf("%d",p); } int f(int a, int b) { int c; if(a>b) c=1; else if(a==b) c=0; else c=-1; return(c); 运行结果:0 运行结果:1
16
对于函数的位置: 对于非int函数,调用单位的位置要在被调用单位在下面,否则编译产生错误。 解决方法是:在调用单位加上被调用函数的声(说)明。 void f() { … } main( ) f ( ); void f( ) { … } main( ) { … f (); void f( ) { … } main( ) {void f( ); f (); …
17
5.2.2 函数说明 对被调用函数要求: 函数说明 必须是已存在的函数 库函数: #include <*.h>
用户自定义函数: 函数类型说明 函数说明 一般形式: 函数类型 函数名(形参类型 [形参名],….. ); 或 函数类型 函数名(); 作用:告诉编译系统函数类型、参数个数及类型,以便检验; 函数定义与函数说明不同; 函数说明位置:程序的数据说明部分(函数内或外); 下列情况下,可不作函数说明 若函数返值是char或int型,系统自动按int型处理; 被调用函数定义出现在主调函数之前; 有些系统(如Borland C++)要求函数说明指出函数返值类型和形参类型,并且对void 和 int 型函数也要进行函数说明
18
{ float add(float,float); /*function declaration*/ float a,b,c;
例 函数说明举例 /* */ main() { float add(float,float); /*function declaration*/ float a,b,c; scanf("%f,%f",&a,&b); c=add(a,b); printf("sum is %f",c); } float add(float x, float y) { float z; z=x+y; return(z); float add(); /*ch7_5.c*/ float add(float x, float y) { float z; z=x+y; return(z); } main() { float a,b,c; scanf("%f,%f",&a,&b); c=add(a,b); printf("sum is %f",c); 被调函数出现在主调函数 之前,不必函数说明 main() { float a,b; int c; scanf("%f,%f",&a,&b); c=max(a,b); printf("Max is %d\n",c); } max(float x, float y) { float z; z=x>y?x:y; return(z); int型函数可不作函数说明 (Borland C++不行)
19
例
20
5.2.3 函数参数及其传递方式 形参与实参 形式参数:定义函数时函数名后面括号中的变量名 实际参数:调用函数时函数名后面括号中的表达式
C语言中,函数调用是值传递方式,即函数的实际参数和形式参数之间的数据传递方向是单向的,只能由实际参数传递给形式参数,而不能由形式参数传递给实际参数,是实际参数向形式参数单向赋值的关系。 在内存中,形式参数与实际参数占用不同的内存单元。当调用函数时,给形式参数分配内存单元,将实际参数的值赋值给形式参数,调用后,形式参数单元释放,实际参数仍保留调用前的值,形式参数值的变化不影响实际参数。
21
例 比较两个数并输出大者 main() { int a,b,c; scanf("%d,%d",&a,&b); c=max(a,b);
printf("Max is %d",c); } max(int x, int y) { int z; z=x>y?x:y; return(z); c=max(a,b); (main 函数) (max 函数) max(int x, int y) { int z; z=x>y?x:y; return(z); } 实参 形参
22
说明: 实参必须有确定的值 形参必须指定类型 形参与实参类型一致,个数相同 若形参与实参类型不一致,自动按形参类型转换———函数调用转换 形参在函数被调用前不占内存;函数调用时为形参分配内存;调用结束,内存释放
23
x 例 计算x的立方 #include <stdio.h> float cube(float x)
{ return(x*x*x); } main() { float a, product; printf("Please input value of a:"); scanf("%f",&a); product=cube(a); printf(”Cube of %.4f is %.4f\n",a,product); a product ×× 1.2 1.2 1.728 x
24
参数传递方式 值传递方式 方式:函数调用时,为形参分配单元,并将实参的值复制到形参中;调用结束,形参单元被释放,实参单元仍保留并维持原值
特点: 形参与实参占用不同的内存单元 单向传递
25
#include <stdio.h> main() { int a=3,b=5;
例 交换两个数 #include <stdio.h> main() { int a=3,b=5; printf(“a=%d,\tb=%d\n",a,b); printf("swapped:\n"); swap(a,b); } swap(int x,int y) { int temp; temp=x; x=y; y=temp; 3 5 a: b: 调用前: 调用 3 5 x: y: a: b: swap: temp 3 5 a: b: x: y: 调用结束: 3 5 a: b:
26
如何实现“地址传递”? 只能是:函数调用时,将数据的存储地址作为参数传递给形参,当然此时的行参只能是能存储地址的变量(指针变量)。才能达到“双向”传送的目的。
27
printf(“a=%d,b=%d\n”,a,b); printf(“swapped:\n”); swap(&a,&b);
例 交换两个数 swap(p1,p2) int *p1,*p2; { int p; p=*p1; *p1=*p2; *p2=p; } main() { int a,b; scanf("%d,%d",&a,&b); printf(“a=%d,b=%d\n”,a,b); printf(“swapped:\n”); swap(&a,&b); printf(”a=%d,b=%d\n",a,b); a 5 9 b 调前: a 5 9 b 调swap: p1 &a &b p2 a 9 5 b 交换: p1 &a &b p2 a 9 5 b 返回:
28
long factorial(int n);
#include <stdio.h> long sum(int a, int b); long factorial(int n); main() { int n1,n2; long a; scanf("%d,%d",&n1,&n2); a=sum(n1,n2); printf("a=%1d",a); } long sum(int a,int b) { long c1,c2; c1=factorial(a); c2=factorial(b); return(c1+c2); long factorial(int n) { long rtn=1; int i; for(i=1;i<=n;i++) rtn*=i; return(rtn); 文件包含编译预处理命令 函数类型说明 long sum(int a, int b); long factorial(int n); 函数调用 实参 函数定义 形参 函数返回值
29
5.3 变量的作用域和存储类别 变量是对程序中数据的存储空间的抽象 main() { int a; a=10;
内存 ……. main() { int a; a=10; printf(“%d”,a); } 编译或函数调用时为其分配内存单元 2000 2001 10 变量在某一时刻存在,则这时属变量的“生存期”(从给变量分配内存至所分配内存被系统收回的时间间隔 程序中使用变量名对内存操作
30
变量定义格式: [存储类型] 数据类型 变量表;
变量的属性 数据类型:变量所持有的数据的性质(操作属性) 存储属性 存储器类型:寄存器、静态存储区、动态存储区 生存期:变量在某一时刻存在 静态变量与动态变量 作用域:变量在某区域内有效 局部变量与全局变量 变量的存储类型 auto 自动型 register-----寄存器型 static 静态型 extern 外部型 变量定义格式: [存储类型] 数据类型 变量表; 如: int sum; auto int a,b,c; register int i; static float x,y;
31
变量的作用域 程序中所声明的在程序的那一部分中是可用的。 只有在自己的作用域中才可用。
32
从作用域角度考虑分为:内部变量、外部变量
内部变量(局部变量)的作用域:是定义它的函数内或复合语句内,在它的作用域之外,内部变量是不可见的,也就是说,一个函数内定义的内部变量是不能被其它的函数所引用的。 特性:有助实现信息隐蔽,即使不同的函数定义了同名的内部变量,也不会相互影响。
33
内部变量---局部变量 float f1(int a) { int b,c; ……. 定义:在函数内定义,只在本函数内有效 } 说明:
char f2(int x,int y) { int i,j; …… main() { int m,n; a,b,c有效 x,y,i,j有效 m,n有效 内部变量---局部变量 定义:在函数内定义,只在本函数内有效 说明: main中定义的变量只在main中有效 不同函数中同名变量,占不同内存单元 形参属于局部变量 可定义在复合语句中有效的变量 局部变量可用存储类型:auto register static (默认为auto)
34
printf("main:a=%d,b=%d\n",a,b); sub(); } sub() a=6; b=7;
例 不同函数中同名变量 main() { int a,b; a=3; b=4; printf("main:a=%d,b=%d\n",a,b); sub(); } sub() a=6; b=7; printf("sub:a=%d,b=%d\n",a,b); 复合语句中变量 #define N 5 main() { int i; int a[N]={1,2,3,4,5}; for(i=0;i<N/2;i++) { int temp; temp=a[i]; a[i]=a[N-i-1]; a[N-i-1]=temp; } for(i=0;i<N;i++) printf("%d ",a[i]); 运行结果: main:a=3,b=4 sub:a=6,b=7 运行结果:
35
外部变量(全局变量) 定义:在函数外面定义的变量。
外部变量的作用域:对于只有一个源程序文件构成的程序,外部变量的作用域是从定义它的位置开始,直至它所在源程序文件的结束。 特点:外部变量的使用增加了函数之间传递数据的途径,在外部变量的作用域内的任何函数都能引用该外部变量,一个函数对外部变量的修改,能影响到其它引用这个变量的函数;因此对外部变量的使用不当,会产生意外的错误。
36
else if(x<min) min=x; sum+=x;} return(sum/n); } main()
float max,min; float average(int n) { int i; float x; scanf(“%f”,&x); max=min=x; for(i=2;i<n;i++) {scanf(“%f”,&x); if(x>max) max=x; else if(x<min) min=x; sum+=x;} return(sum/n); } main() { int n; float ave; scanf(“%d”,&n); ave=average(n); printf("max=%6.2f\nmin=%6.2f\n average=%6.2f\n",max,min,ave); 作用域 max min
37
外部变量与局部变量同名时,在内部变量的作用域中, 外部变量被屏蔽。 例:
int a=3,b=5; max(int a, int b) { int c; c=a>b?a:b; return(c); } main() { int a=8; printf(“a=%d\n,b=%d\n,max=%d\n", a,b,max(a,b)); 运行结果: a=8 /*main中的a*/ b=5 /*main中的b*/ max=8
38
外部变量使用不当时,会引起副作用。 int i; main() { void prt(); for(i=0;i<5;i++)
} void prt() { for(i=0;i<5;i++) printf(“%c”,’*’); printf(“\n”); 运行结果:*****
39
外部变量的作用域可以通过关键字“extern”来扩展:
int max(int x, int y) { int z; z=x>y?x:y; return(z); } main() { extern int a,b; printf("max=%d",max(a,b)); int a=13,b=-8; extern int a,b; int max() { int z; z=a>b?a:b; return(z); } main() { printf("max=%d",max()); int a=13,b=-8; 运行结果:max=13
40
int p=1,q=5; float f1(int a) { int b,c; ……. } int f3() {….. char c1,c2; char f2(int x,int y) { int i,j; …… main() { int m,n; c1,c2的作用范围 p,q的作用范围 extern char c1,c2; c1,c2 的作用范围 扩展后 c1,c2 的作用范围 扩展后 extern char c1,c2;
41
用extern扩展外部变量作用域例子: main() { void gx(),gy(); extern int x,y; printf(“1: x=%d\ty=%d\n”,x,y); y=246; gx(); gy(); } void gx() { extern int x,y; x=135; printf(“2: x=%d\ty=%d\n”,x,y); int x,y; void gy() { printf(“3: x=%d\ty=%d\n”,x,y); 运行结果: 1: x= y=0 2: x= y=246 3: x= y=246
42
extern int global; /*说明*/ int number; /*定义*/ func2() { .
外部变量(全局变量)继续: 对于多文件程序,在一个源文件中定义的外部变量还可以在其它的文件中使用,这时需要在使用它的文件中用extern进行说明,这样的变量称为程序级的变量。 int global;/*定义*/ extern float x; /*说明*/ main() { int local; . } extern int global; /*说明*/ int number; /*定义*/ func2() { . float x; /*定义*/ func3() { extern int global; file1.c file2.c file3.c
43
外部变量定义与外部变量说明不同: 外部变量说明: extern 数据类型 变量表;
在同一个文件中,定义在后使用在前的外部变量,在使用前需要对其进行声明。 在包含多个文件的程序中,一个文件若使用其它文件中定义的外部变量也要进行声明。 外部变量说明: extern 数据类型 变量表; 应尽量少使用全局变量,因为: 全局变量在程序全部执行过程中占用存储单元 降低了函数的通用性、可靠性,可移植性 降低程序清晰性,容易出错 定义 说明 次数: 只能1次 可说明多次 位置: 所有函数之外 函数内或函数外 分配内存: 分配内存,可初始化 不分配内存,不可初始化
44
C语言中,按作用域的大小,可将变量的作用域分为四个级别:程序级、文件级、函数级、复合语句级。这是由C程序的结构特点所决定的。
45
5.3.2 变量的存储类别------动态变量与静态变量
变量的存储类别------动态变量与静态变量 动态变量:动态存储类别的变量当进入定义它的函数或复合语句时被分配存储空间,当离开时所占内存空间被释放。 静态变量:静态存储类别的变量在源程序编译的时候被分配固定的存储空间,从程序开始执行到程序运行结束,一直占用该内存空间,直至程序运行结束,才被释放内存空间。 程序区 静态存储区 动态存储区 全局变量、局部静态变量 形参变量 局部动态变量(auto register) 函数调用现场保护和返回地址等 生存期------变量存在(在内存有存储空间)的时间段。 静态变量:从程序开始执行到程序结束 动态变量:从包含该变量定义的函数开始执行至函数执行结束
46
5.3.3 内部变量的存储类别 内部变量的作用域是定义它的函数或复合语句。内部变量的存储类别是指它存放的位置。内部变量可存放于内存的动态区,寄存器和内存的静态区。但无论内部变量存放在何处,它的作用域是不变的。 内部变量可以定义为: 自动的(auto ) 寄存器(register ) 静态的(static ) 自动的(auto ):在函数内定义的变量都是自动的。 main() {int a; /*等价于:auto int a;*/ … } 寄存器变量(register ) 如:register int i; 动态变量 静态变量
47
例 auto 变量的作用域 main() { int x=1; void prt(void); { int x=3; prt(); printf(“2nd x=%d\n”,x); } printf(“1st x=%d\n”,x); void prt(void) { int x=5; printf(“3th x=%d\n”,x); x=1作用域 x=3作用域 运行结果: 3th x=5 2nd x=3 1st x=1 x=5作用域
48
分配内存后、赋初值,并且只被赋初值一次,未赋值的内部staic变量,系统自动给它赋值为0。
静态的(static ): 如:static int i; 被分配在内存的静态存储区中。 分配内存后、赋初值,并且只被赋初值一次,未赋值的内部staic变量,系统自动给它赋值为0。 staic变量在内存的静态存储区占用的固定的内存单元;即使它所在的函数被调用结束后,也不释放存储单元,它所在单元的值也会继续保留-----因此: 下次再调用该函数时,staic变量仍使用原来的存储单元,仍使用原来存储单元中的值。 虽然staic变量在整个程序运行期间都是存在的,但在它的作用域外,它是不可见的,也就是说其它函数是不能引用它的。
49
{ void increment(void); increment();
例 局部静态变量值具有可继承性 main() { void increment(void); increment(); } void increment(void) { int x=0; x++; printf(“%d\n”,x); main() { void increment(void); increment(); } void increment(void) { static int x=0; x++; printf(“%d\n”,x); 运行结果:1 1 运行结果:1 2 3
50
{auto int auto_v=0; /*内部自动的*/ static int static_v=0; /*内部静态的*/
例 5.8 #include"stdio.h" void test_a_s() {auto int auto_v=0; /*内部自动的*/ static int static_v=0; /*内部静态的*/ printf("\nauto_v=%d,static_v=%d",auto_v,static_v); auto_v++; static_v++; } main() {int i; for(i=0;i<3;i++) test_a_s(); 运行结果: auto_v=0,static_v=0 auto_v=0,static_v=1 auto_v=0,static_v=2
51
例 5.9计算 1!、2!、3!、……n!(n的值由键盘输入)
不利用静态变量的情况: long func(long n) { long k=1;int i; for(i=1;i<=n;i++) k=k*i; return (k); } main() {long n,m; printf("\n"); scanf("%ld",&n); for (m=1L;m<=n;m++) printf("\n%ld!=%ld",m,func(m)); 利用静态变量的情况: long func(long i) {static long k=1; k=k*i; return (k); } main() {long n,m; printf("\n"); scanf("%ld",&n); for (m=1L;m<=n;m++) printf("\n%ld!=%ld", m,func(m));
52
外部变量只能存放在内存的静态存储区 。它在整个程序的运行期间一直占用内存单元。
5.3.4 外部变量的存储类别 外部变量只能存放在内存的静态存储区 。它在整个程序的运行期间一直占用内存单元。 外部变量的作用域可以通过关键字“extern”来扩展。 (扩展至某个文件,甚至被其它源程序文件使用) 用static声明的外部变量能够限制它的作用域的扩展,达到信息隐蔽的目的。 ① 在同一个文件中,定义在后使用在前的外部变量,在使用前需要对其进行声明。 ② 在不同文件中(但同属一个程序),使用其它文件中定义的外部变量(但是不要企图使用static声明了的外部变量)。
53
例 5.10 该程序包括两个文件p5-1001.c和p5-1002.c,并且文件
p c使用p c中的外部变量,因此要在文件p c中对该外部变量进行声明。 /*p c*/ int Max; void func(int x,int y,int z) {extern int Min; Max=x; Min=x; if(y>Max) Max=y; if(z>Max) Max=z; if(y<Min) Min=y; if(z<Min) Min=z; } int Min; /*p c*/ #include"p c" extern int Max,Min; main() {void func(int x,int y,int z); int a,b,c; printf("\ a,b,c=?"); scanf("%d,%d,%d",&a,&b,&c); func(a,b,c); printf("\nMax=%d,Min=%d",Max,Min); }
54
变量存储类型 局部变量 外部变量 register 局部static auto 外部static 外部 存储类别 静态 动态 存储方式
静态存储区 动态区 存储区 寄存器 程序整个运行期间 函数调用开始至结束 生存期 作用域 定义变量的函数或复合语句内 本文件 其它文件 编译时赋初值,只赋一次 每次函数调用时 赋初值 自动赋初值0或空字符 不确定 未赋初值 局部变量默认为auto型 register型变量个数受限,且不能为long, double, float型 局部static变量具有全局寿命和局部可见性 局部static变量具有可继承性 extern不是变量定义,可扩展外部变量作用域
55
例 文件file1.c int a; main( ) { ……. ……. f2; f1; } f1( ) { auto int b; ………
{ ……. ……. f2; f1; } f1( ) { auto int b; ……… …….. f2( ) { static int c; a作用域 main f2 f1 a生存期: b生存期: c生存期: b作用域 C作用域
56
#include <stdio.h> int i=1; main() { static int a;
例 变量的寿命与可见性 Main------ i:1 a:0 b:-10 c:0 全局i 1 main: a b:-10 register main:c 静态 存储区 动态 other: a 2 other: b 43 33 75 #include <stdio.h> int i=1; main() { static int a; register int b=-10; int c=0; printf("-----MAIN------\n"); printf("i:%d a:%d \ b:%d c:%d\n",i,a,b,c); c=c+8; other(); printf("i:%d a:%d \ i=i+10; } ------Other------ i:33 a:4 b:0 c:15 4 6 6 4 Main----- i:33 a:0 b:-10 c:8 Other i:75 a:6 b:4 c:15 other: c 10 other: c 10 15 15 8 other() { static int a=2; static int b; int c=10; a=a+2; i=i+32; c=c+5; printf("-----OTHER------\n"); printf("i:%d a:%d \ b:%d c:%d\n",i,a,b,c); b=a; }
57
例 引用其它文件中的变量,输出ab和a的m次方
int a; main() { int power(int n); int b=3,c,d,m; printf("Enter the number a and its power:\n"); scanf("%d,%d",&a,&m); c=a*b; printf("%d*%d=%d\n",a,b,c); d=power(m); printf("%d**%d=%d",a,m,d); } extern int a; int power(int n) { int i,y=1; for(i=1;i<=n;i++) y*=a; return(y);
58
5.4 内部函数和外部函数 5.4.1 外部函数 外部函数是可以被程序中的其它文件所调用的函数。 定义格式如下:
extern 数据类型 函数名(形式参数表列) { 说明部分; 执行部分; } 外部函数是C语言默认的函数类型,若没有特别的声明为extern类型,系统也会默认为外部函数。
59
5.4.2 内部函数 内部函数是只能被本文件中其它函数调用,而不能被其它文件调用的函数。 它的定义格式如下: static 数据类型 函数名(形式参数表列) { 说明部分; 执行部分; }
60
5.5 函数的嵌套与递归调用 嵌套调用 C规定:函数定义不可嵌套,但可以嵌套调用函数 main( ) 调用函数a 结束 a函数 b函数
61
int dif(int x,int y,int z) { return max(x,y,z)-min(x,y,z); }
例 求三个数中最大数和最小数的差值 int dif(int x,int y,int z) { return max(x,y,z)-min(x,y,z); } int max(int x,int y,int z) { int r; r=x>y?x:y; return(r>z?r:z); } int min(int x,int y,int z) { int r; r=x<y?x:y; return(r<z?r:z); #include <stdio.h> int dif(int x,int y,int z); int max(int x,int y,int z); int min(int x,int y,int z); void main() { int a,b,c,d; scanf("%d%d%d",&a,&b,&c); d=dif(a,b,c); printf("Max-Min=%d\n",d); } main( ) 调用函数dif 输出 结束 dif函数 max函数 调用函数max 调用函数min min函数
62
例 用弦截法求方程根 x y f(x) x1 x2 f(x1) f(x2)
63
y=f(x),y1=f(x1) 运行情况: Input x1,x2: 2,6 A root of equation is 5.0000
求f(x1)与f(x2)连线与x轴的交点x 输入x1,x2,求f(x1),f(x2) 直到f(x1)与f(x2)异号 y=f(x),y1=f(x1) y与y1同号 真 假 x1=x y1=y x2=x 直到 |y|< root=x 输出 root root函数 运行情况: Input x1,x2: 2,6 A root of equation is 1.函数定义相互独立 2.因为婚书定义出现在main之前,不必函数说明 3.嵌套调用 4.fabs main( ) 调用函数root 输出根 x 结束 root函数 xpoint函数 调用函数xpoint 调用函数f f函数
65
递归调用 定义:函数直接或间接的调用自身叫函数的递归调用 说明 int f(int x) { int y,z; …… z=f(y); …….
return(2*z); } int f1(int x) { int y,z; …… z=f2(y); ……. return(2*z); } int f2(int t) { int a,c; c=f1(a); return(3+c); f( ) 调f 调f2 调f1 f1( ) f2( ) 说明 C编译系统对递归函数的自调用次数没有限制 每调用函数一次,在内存堆栈区分配空间,用于存放函数变量、返回值等信息,所以递归次数过多,可能引起堆栈溢出
66
/*ch7_8.c*/ #include <stdio.h> int fac(int n) { int f; if(n<0) printf("n<0,data error!"); else if(n==0||n==1) f=1; else f=fac(n-1)*n; return(f); } main() { int n, y; printf("Input a integer number:"); scanf("%d",&n); y=fac(n); printf("%d! =%15d",n,y); 例 求n的阶乘
67
Hanoi(汉诺)塔问题: 古代有一个梵塔,塔内有三各底座A、B、C。开始时A座上有64个盘子,牌子大小不等,大的在下,小的在上。有一个老和尚想把这64个盘子从A座移到C座上去,每一次只允许移动一个盘子,且在移动过程中每个底座上的盘子必须是大的在下,小的在上。
68
老和尚自然这样想:假若有另外一个和尚想办法将63个盘子从一个地座上,那么问题就解决了。老和尚只需这样做:
(1)命令第二个和尚将63个盘子从A移到B座; (2)自己将最底下的最大的那一个盘子从A移到C座; (3)再命令第二个和尚将63个盘子从B移到C座; 任务就完成了。
69
第二个和尚自然这样想:假若有第三个和尚想办法将62个盘子从一个地座上,那么问题就解决了。第二个和尚只需这样做:
(1)命令第三个和尚将62个盘子从A移到C座; (2)自己将最底下的最大的那一个盘子从A移到B座; (3)再命令第二个和尚将62个盘子从C移到B座; 这样第二个和尚的任务就完成了。
70
这样以此类推,层层下放,直到第63个和尚找到第64个和尚,第64个和尚的任务是将一个盘子移到另一座上,第63个和尚再完成自己的任务;第62个和尚再完成自己的任务;…;老和尚才能完成自己的任务。
以3个盘子为例,步骤为: A->C, A->B, C->B, A->C, B->A, B->C, A->C
71
void move(char getone, char putone)
{ printf("%c--->%c\n",getone,putone); } void hanoi(int n,char one,char two,char three) { if(n==1) move(one,three); else { hanoi(n-1,one,three,two); move(one,three); hanoi(n-1,two,one,three); } main() { int m; printf("Input the number of disks:"); scanf("%d",&m); printf("The steps to moving %3d disks:\n",m); hanoi(m,'A','B','C'); 例5.14 Hanoi问题 A B C
72
6.7 数组作为函数参数 数组元素作函数实参——值传递 #include <stdio.h> main()
{ 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("Enter array b:\n"); scanf("%d",&b[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; } /* Output */ int large(int x,int y) { int flag; if(x>y) flag=1; else if(x<y) flag=-1; else flag=0; return(flag); 例 两个数组大小比较 4 3 2 1 5 a 56 23 12 10 76 88 b 21 43 98 66 54 a和b为有10个元素的整型数组 比较两数组对应元素 变量n,m,k记录a[i]>b[i], a[i]==b[i], a[i]<b[i]的个数 最后 若n>k,认为数组a>b 若n<k,认为数组a<b 若n==k,认为数组a==b i n=0 m=0 k=1 i n=0 m=1 k=1 i n=1 m=1 k=1 i n=1 m=1 k=2 i n=2 m=1 k=2 i n=3 m=1 k=2 n=0 m=0 k=0
73
数组名作函数参数 地址传递 在主调函数与被调函数分别定义数组,且类型应一致 形参数组大小(多维数组第一维)可不指定 形参数组名是地址变量
74
#include <stdio.h> float average(int stu[10], int n);
例 求学生的平均成绩 #include <stdio.h> float average(int stu[10], int n); void main() { int score[10], i; float av; printf("Input 10 scores:\n"); for( i=0; i<10; i++ ) scanf("%d", &score[i]); av=average(score,10); printf("Average is:%.2f", av); } float average(int stu[10], int n) { int i; float av,total=0; for( i=0; i<n; i++ ) total += stu[i]; av = total/n; return av; } . 2 1 9 score 56 23 12 …. 88 stu 实参用数组名
75
#include <stdio.h> void swap2(int x,int y) { int z;
z=x; x=y; y=z; } main() { int a[2]={1,2}; swap2(a[0],a[1]); printf("a[0]=%d\na[1]=%d\n",a[0],a[1]); 例 数组元素与 数组名 作函数参数比较 值传递 1 2 a 调用 a[0] a[1] x y 2 1 x y 交换 1 2 a 调用前 a[0] a[1] 1 2 a 返回
76
#include <stdio.h> void swap2(int x[]) { int z;
z=x[0]; x[0]=x[1]; x[1]=z; } main() { int a[2]={1,2}; swap2(a); printf("a[0]=%d\na[1]=%d\n",a[0],a[1]); 例 数组元素与 数组名 作函数参数比较 地址传递 1 2 a 调用前 1 2 a x 调用 2 1 a x 交换 2 1 a 返回
77
a k array j j i=0 j k j k j j j j j main() { int a[10],i;
void sort(int array[],int n) { int i,j,k,t; for(i=0;i<n-1;i++) { k=i; for(j=i+1;j<n;j++) if(array[j]<array[k]) k=j; if(k!=i) { t=array[i]; array[i]=array[k]; array[k]=t; } main() { int a[10],i; for(i=0;i<10;i++) scanf("%d",&a[i]); sort(a,10); printf("%d ",a[i]); printf("\n"); 例 数组排序----简单选择排序 1 2 3 4 5 6 7 8 9 a 9 k array 49 68 57 32 9 99 27 13 76 88 j j i=0 j k 49 j k j j j j j
78
a array k j k i=1 j k j j j k j k j j main() { int a[10],i;
void sort(int array[],int n) { int i,j,k,t; for(i=0;i<n-1;i++) { k=i; for(j=i+1;j<n;j++) if(array[j]<array[k]) k=j; if(k!=i) { t=array[i]; array[i]=array[k]; array[k]=t; } main() { int a[10],i; for(i=0;i<10;i++) scanf("%d",&a[i]); sort(a,10); printf("%d ",a[i]); printf("\n"); 例 数组排序----简单选择排序 1 2 3 4 5 6 7 8 9 a 49 68 57 32 99 27 13 76 88 array 13 k j k i=1 j k j j j k 68 j k j j
79
a array i=8 main() { int a[10],i; 例 数组排序----简单选择排序
void sort(int array[],int n) { int i,j,k,t; for(i=0;i<n-1;i++) { k=i; for(j=i+1;j<n;j++) if(array[j]<array[k]) k=j; if(k!=i) { t=array[i]; array[i]=array[k]; array[k]=t; } main() { int a[10],i; for(i=0;i<10;i++) scanf("%d",&a[i]); sort(a,10); printf("%d ",a[i]); printf("\n"); 例 数组排序----简单选择排序 1 2 3 4 5 6 7 8 9 a 13 27 32 49 57 68 76 88 99 array i=8
80
int max_value(int array[3][4]) { int i,j,k,max; max=array[0][0];
例 求二维数组中最大元素值 i j max=1 int max_value(int array[3][4]) { int i,j,k,max; max=array[0][0]; for(i=0;i<3;i++) for(j=0;j<4;j++) if(array[i][j]>max) max=array[i][j]; return(max); } main() { int a[3][4]={{1,3,5,7}, {2,4,6,8},{15,17,34,12}}; printf("max value is %d\n",max_value(a)); i j max=3 i j max=5 多维形参数组第一维维数 可省略,第二维必须相同 int array[][4] j i max=7 j i max=7 j i max=34
81
get_sum_row(int x[][3], int result[] ,int row, int col) { int i,j;
例 求二维数组中各行元素之和 get_sum_row(int x[][3], int result[] ,int row, int col) { int i,j; for(i=0;i<row;i++) { result[i]=0; for(j=0;j<col;j++) result[i]+=x[i][j]; } main() { int a[2][3]={3,6,9,1,4,7}; int sum_row[2],row=2,col=3,i; get_sum_row(a,sum_row,row,col); printf("The sum of row[%d]=%d\n",i+1,sum_row[i]); x result 3 1 4 6 7 9 a sum_row 18 12
Similar presentations