




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、第9章 函数C 语言程序设计进阶篇第1页第1页内容概述函数定义、函数原型及申明。函数调用及参数传递。函数嵌套概念及其定义和使用办法。函数递归概念及其定义和使用办法。局部变量、全局变量以及最惯用两种变量存储类型auto和static。第2页第2页教学目的掌握自定义函数普通结构及定义函数办法; 掌握形参、实参、函数原型等主要概念。掌握函数申明、函数调用普通办法;熟悉函数嵌套概念,能定义和使用嵌套函数。理解局部变量、全局变量和变量存储类型概念,掌握auto型和static型局部变量特点和使用办法。 理解main函数参数及使用办法。第3页第3页 在进行大型程序设计时,采用模块化程序设计是一个十分有效办
2、法。C语言在支持模块化程序设计方面含有很强优势。C语言将函数作为程序设计基本单元,每个函数用于描述一个相对独立基本操作,函数之间通过参数传递数据。采用模块化程序设计思想可减少程序设计复杂度,提升程序可维护性。第4页第4页 求解较小问题算法和程序称作“功效模块”, 各功效模块能够先单独设计,然后将求解所有子问题模块组合成求解原问题程序。 由功效模块构成程序结构图主控模块模块1_1模块1_n模块2_1模块2_n模块n_1模块n_n模块1模块2模块n第5页第5页例 输入两个数输出其中一个大数main() int a,b,c; int max(int x,int y) ; printf(”a= b=“
3、); scanf(“%d, %d”,&a,&b); c=max(a,b); printf(“max=%d”,c) ;int max(int x,int y) int z ; if (xy) z=x ; else z=y ; return (z) ;第6页第6页例 输入年月日计算出该日为该年第几天。 分析:(1)判断年份是否为闰年。年份有闰年与平年之分,二者区分在于闰年二月为29天,平年二月为28天。因此,给定一个年份,首先应确定其是否是闰年。(2)求月份对应天数。月份不同,其对应天数不同,1、3、5、7、8、10、12月每个月为31天,4、6、9、11月每个月为30天,2月依据所在年份是否为闰
4、年来确定。(3)求总天数。分为经历完整月份天数与经历不完整月份天数。(4)输出数据。输出年月日及对应天数。第7页第7页(1)判断闰年。int leap(int year) int lp; lp=(year%4=0&year%100!=0|year%400=0)?1:0; return lp;主控模块判断闰年求某月天数输 出输 入求总天数程序模块结构图第8页第8页(2)求某月天数。/*函数month_days调用函数leap判断year是否为闰年*/int month_days(int year,int month) int d; switch(month) case 1: case 3: ca
5、se 5: case 7: case 8: case 10: case 12: d=31; break;case 2: d=leap(year)?29:28; break; /*若为闰年,d赋值29,不然赋值28*/ default: d=30; return d;第9页第9页(3)求天数和。/*函数days调用函数month_days,求各月份相应天数*/int days(int year,int month,int day) int i,ds=0; for (i=1;imonth;i+) ds=ds+month_days(year,i); ds=ds+day; return ds;第10页
6、第10页注意:在完整程序中,前三个函数应放在main( )函数之前。(4)主函数。void main() int year,month,day,t_day; printf(Input year-month-day:n); scanf(%d-%d-%d,&year,&month,&day); t_day=days(year,month,day); /*求天数和*/ printf(%d-%d-%d is %dth day of the year!n,year,month,day,t_day);第11页第11页 C语言源程序是由函数构成,函数是C源程序基本模块,通过对函数模块调用实现特定功效。C语言
7、中函数相称于其它高级语言子程序。C语言不但提供了极为丰富库函数,还允许用户自己定义函数。9.1 函数概述第12页第12页在C语言中可从不同角度对函数分类: 1从函数定义角度分类库函数由C系统提供,用户不必定义,也不必在程序中作类型阐明,只需在程序前包括有该函数原型头文献即可在程序中直接调用。用户定义函数由用户按需要编写函数。对于用户自定义函数,不但要在程序中定义函数本身,并且在主调函数模块中还必须对该被调函数进行类型阐明,然后才干使用。第13页第13页有返回值函数 有返回值函数被调用执行完后将向调用者返回一个执行结果,称为函数返回值。无返回值函数 无返回值函数用于完毕某项特定处理任务,执行完毕
8、后不向调用者返回函数值。2从函数功效角度分类:第14页第14页3从主调函数和被调函数之间数据传送角度分类:无参函数 函数定义、函数阐明及函数调用中均不带参数。主调函数和被调函数之间不进行参数传送。这类函数通惯用来完毕一组指定功效,能够返回或不返回函数值。有参函数 有参函数也称为带参函数。在函数定义及函数阐明时都有参数,称为形式参数(简称为形参)。在函数调用时也必须给出参数,称为实际参数(简称为实参)。第15页第15页1. 在C语言中,所有函数都是平等,即函数不能嵌套定义。但是函数之间允许互相调用,即允许嵌套调用。2. main()函数是程序执行起点,也是程序执行终点。3.一个C源程序有且仅有一
9、个主函数main()。阐明:第16页第16页 阐明部分; 执行部分; 函数由函数阐明与函数体两部分构成。 阐明部分; 执行部分; 9.2 函数定义函数阐明函数体函数阐明函数体1无参函数类型阐明符 函数名()2有参函数类型阐明符 函数名(形式参数表)第17页第17页例 调用函数在屏幕上打印一行“*”#include stdio.hvoid print() /* 无参函数 */ printf(*);main() print();程序运营结果:*第18页第18页#include stdio.hvoid print(int n) /* 一个形参 */ int i; for(i=0;in;i+) pri
10、ntf(*);main() int n; scanf(%d,&n); print(n); /* 一个实参 */例 调用函数在屏幕上打印指定数目“*”程序运营结果:10*第19页第19页#include stdio.hvoid print(int n,char ch) /* 两个形参 */ int i; for(i=0;in;i+) printf(%c,ch);main() int n; char ch; scanf(%d %c,&n,&ch); print(n,ch); /* 两个实参 */例 调用函数在屏幕上打印指定数目任意符号程序运营结果:10 $第20页第20页9.3.1 形式参数与实际
11、参数 函数参数分为形式参数和实际参数两种。在定义函数时函数名后面括号中变量称为形式参数,简称形参。在调用函数时,函数名后面括号中表示式称为实际参数,简称实参。形式参数(简称形参)定义函数使用参数实际参数(简称实参)调用函数使用参数9.3 函数参数和返回值第21页第21页【例9.1】求 1+2+3+n值。#include stdio.hint s(int n) int i,s=0; for(i=1;i=1;i-) n=n+i; printf(n=%dn,n);void main() int n; printf(input numbern); scanf(%d,&n); s(n); printf(
12、n=%dn,n);程序运营情况下列:input number100n=5050n=100第23页第23页函数main函数snn实参与形参数据传递通过函数调用,将实参值传递给形参. 函数调用时,系统对参数处理环节为:计算各实参值,将实参值压入形参栈中,然后执 行函数体。当函数执行完返回时,形参从栈顶弹出(取走)。 第24页第24页函数形参和实参含有下列特点: 形参变量只有在被调用时才分派内存单元,在调用结束时,马上释放所分派内存单元。 实参能够是常量、变量、表示式、函数等,无论实参是何种类型量,在进行函数调用时,它们都必须含有拟定值,以便把这些值传送给形参。 实参和形参在数量上、类型上、顺序上应
13、严格一致,不然会发生“类型不匹配”错误。 函数调用中发生数据传送是单向。第25页第25页 C函数计算结果通过 return语句返回, return语句表示形式下列:return 表示式;或 return(表示式);9.3.2 函数返回值 函数返回值也就是函数值,是指函数被调用之后,执行函数体中程序段所取得并返回给主调函数值。第26页第26页对函数值有下列一些阐明:函数值只能通过return语句返回给主调函数。函数值类型和函数定义中函数类型应保持一致。假如两者不一致,则以定义时函数类型为准,自动进行类型转换。如函数值为整型,在函数定义时能够省去类型阐明。不返回函数值函数,能够明拟定义为“空类型”
14、,类型阐明符为“void”。第27页第27页 一个函数一旦被定义,就可在程序其它函数中使用它,这个过程称为函数调用。9.4.1 函数普通调用函数名(实际参数列表)函数调用形式:9.4 函数调用 实际参数普通为表示式,能够是常量、变量(调用时必须有拟定值或拟定地址)。而形式参数必须为变量。第28页第28页函数表示式 函数作为表示式中一项出现在表示式中,以函数返回值参与表示式运算。这种方式要求函数是有返回值。 如 sum=s(n);函数语句 函数调用普通形式加上分号即构成函数语句。 如 printf(); scanf(); print();函数实参 函数作为另一个函数调用实际参数出现。 如 pri
15、ntf(“sum=%d”,s(n);C语言中,能够用下列几种方式调用函数:第29页第29页9.4.2 实参到形参数据传递方式传值方式传递数据 函数调用时,调用函数把实参值复制一份,传送给被调用函数形参,从而实现调用函数向被调用函数数据传送。实参对形参数据传送是单向。传址方式传递数据 函数调用时,将实参地址传给被调用函数,被调用函数从传过来地址中取出实参所存数据进行操作。在传址方式中,实参和形参占用是同一段内存单元,因此形参值修改实际是就是对实参值修改。第30页第30页9.4.3 函数申明和函数原型 调用用户自定义函数时,普通调用函数和被调用函数应在同一个文献中,在调用函数中对被调用函数返回值类
16、型、函数名称、函数形式参数类型进行阐明,这种阐明称为函数申明。函数申明普通形式下列:类型名 函数名(类型1 形参1,类型n 形参n);或类型名 函数名(类型1,类型2,类型n);或类型名 函数名();函数申明是以语句形式出现,因此其后有语句结束标识“;”。第31页第31页 假如被调函数返回值是整型或字符型时,能够不对被调函数作阐明而直接调用。这时系统将自对被调函数返回值按整型处理。 当被调函数函数定义出现在主调函数之前时,在主调函数中也能够不对被调函数再作阐明而直接调用。 如在所有函数定义之前,在函数外预先阐明了各个函数类型,则在以后各主调函数中,可不对被调函数作阐明。 对库函数调用不需要再作
17、阐明,但必须把该函数头文献用include命令包括在源文献前部。C语言要求存在下列几种情况时,能够省去主调函数中对被调函数函数阐明:第32页第32页例 编程求两个整数阶乘之和。#include void main() int m1,m2; long result; long fac(int); /* 申明后面定义函数fac*/ printf(请输入整数m1,m2: ); scanf(%d,%d,&m1, &m2); result=fac(m1)+fac(m2); /* 函数调用 */ printf(%d!+%d!=%ld n, m1,m2,result);第33页第33页long fac(in
18、t n) /*定义函数fac,其功效是求n!*/ int i; long r=1; for(i=1;i=n;i+) r=r*i; return r;运营情况下列:请输入整数m1,m2:3,53!+5!=126第34页第34页 所谓函数嵌套调用是指一个函数调用另一函数过程中又出现对其它函数调用。 这种嵌套调用层次原则上不限制。 函数1 函数2 函数3 . . 调用函数2 调用函数3 . 9.5函数嵌套调用第35页第35页【例9.2】计算s=22!+32!程序分析: 本题可编写两个函数:一个是用来计算平方值函数f1();另一个是用来计算阶乘值函数f2()。主函数先调f1()函数计算出平方值,再在f
19、1()函数中以平方值为实参,调用f2()函数计算其阶乘值,然后返回到f1()函数,再返回主函数,在循环程序中计算累加和。第36页第36页#include stdio.hlong f2(int q) long c=1; int i; for(i=1;i=q;i+) c=c*i; return c;long f1(int p) int k; long r; k=p*p; r=f2(k); return r;程序代码下列:void main() int i; long s=0; for (i=2;i=3;i+) s=s+f1(i); printf(ns=%ldn,s);第37页第37页9.6 函数递
20、归调用 一个函数在执行过程中又直接或间接调用本身过程称为递归调用,这种函数称为递归函数。在递归调用中,主调函数又是被调函数,执行递归函数将重复调用其本身。第38页第38页递归不是一个数据结构,而是一个有效算法设计注意:递归算法必须是逐步有规律简化,最后 要有一个非递归出口,不能出现无穷调用情况。第39页第39页阶乘递归定义:(1)5!=5 4!(2)4!=4 3!(3) 3!=3 2!(4) 2!=2 1!(5) 1!=1 0!(6) 0!=1【例9.3】用递归调使用办法计算n!第40页第40页long int fact(int n) int x; long int y; if (n=0) r
21、eturn 1; x=n-1; y=fact(x); return(n*y);用递归求阶乘C语言实当代码下列:#include void main() long int fn; fn=fact(5); printf(”nfn=%ld”,fn);第41页第41页求解阶乘 5! 过程主程序 main : fact(5)参数 5 计算 5*fact(4) 返回 120参数 4 计算 4*fact(3) 返回 24参数 3 计算 3*fact(2) 返回 6参数 2 计算 2*fact(1) 返回 2参数 1 计算 1*fact(0) 返回 1参数传递结果返回递归调用回归求值参数 0 直接定值 = 1
22、 返回 1第42页第42页n=5 5! 5*4! 4*3! 3*2! 2*1! 1 1 1*2 2*3 6*4 24*5120回推递推递归结束条件n!= (n=1)n*(n-1)! (n1)第43页第43页【例9.4】用递归法将一个整数n逆序输出。程序分析: 首先定义一个递归函数convert()。在主函数中调用convert()。convert()函数执行过程是:程序首先将整数n最低位数字转换为字符后并输出;假如n10将结束函数执行,不然就递归调用convert()函数本身。由于每次递归调用实参为n/10,即把n/10值赋予形参n,最后当n值小于10时不再作递归调用,形参n值也小于10,即i
23、=0,将使递归终止,然后逐层退回。第44页第44页#include stdio.h void convert(int n) int i; putchar(n%10+0); if (i=n/10)!=0) convert(i); void main() int number; printf(Input an integer:); scanf(%d,&number); if (number0) putchar(-); number=-number; convert(number); putchar(n); 程序代码下列:第45页第45页补充:Hanoi塔问题 假设有三根木桩分别为A、B和C。在木桩
24、A上安置了N个圆盘,由上到下编号为1,2,N,编号越大圆盘直径也越大。现需要将A木桩上N个圆盘借助B木桩移到C木桩上,且必须按照下述移动规则: 1. 直径较小圆盘永远置于直径比较大圆盘上; 2. 圆盘可任意地由任何一个木桩移到其它木桩上; 3. 一次只能移动一个盘子。第46页第46页第47页第47页 汉诺塔(Tower of Hanoi)问题解题思绪: 假如 n=1,则将这一个盘子直接从A柱移到C柱上。否则,执行下列三步: 1.用C柱做过渡,将A柱上(n-1)个盘子移到B柱上; 2.将A柱上最后一个盘子直接移到C柱上; 3.用A柱做过渡,将B柱上(n-1)个盘子移到C柱上。第48页第48页#i
25、nclude void Hanoi(int n,char x,char y,char z) if(n=1)printf(Move disk %d from %c to %cn,n,x,z);else Hanoi(n-1, x, z, y);printf(Move disk %d from %c to %cn,n,x,z);Hanoi(n-1, y, x, z);void main( ) int num;char one,two,three;scanf(%d ,&num);scanf(%c %c %c,&one,&two,&three);Hanoi(num,one,two,three);第49页
26、第49页变量数据类型变量应占用内存空间大小变量在存储空间分派时所限定边界条件变量性质变量数据类型变量存储类型变量作用域变量生存期共同决定9.7变量作用域及存储特性第50页第50页1. 变量按作用域:分为全局变量和局部变量2. 区别:见下表全局变量(外部变量)局部变量(内部变量)定义位置函数体外函数体内作用域从定义处到文献结束从定义处到本函数结束举例所有在函数体外定义变量(1) 所有在函数体内定义变量(2)形式参数注意与局部变量同名处理不同样函数中同名局部变量互不干扰9.7.1. 变量作用域 第51页第51页课堂练习1:分析下面程序运营结果。void f1( ) int t=2; a *= t;
27、 b /= t; void main( ) int a, b; printf(”Enter a,b:”); scanf(”%d,%d”, &a, &b); f1( ); /* 调用函数f1( ) */ printf (”a=%d,b=%d ”, a, b); 编译程序会提醒犯错: undefined symbol a 和 undefined symbol b 。为何?第52页第52页课堂练习2:分析下面程序运营结果 #include int a=2,b=4; /*a,b为全局变量*/ void f1( ) int t1,t2; t1 = a * 2; t2 = b * 3; b = 100;
28、printf (” t1=%d,t2=%dn”, t1, t2); void main() int b=4; f1( ); printf (” a=%d,b=%d ”, a, b); 程序输出结果为: t1=4,t2=12 a=2, b=4 结论:全局变量与局部变量同名时,局部变量作用域屏蔽全局变量第53页第53页9.7.2 变量存储特性 变量存储特性变量生存期变量存储类型变量存在时间,即变量存在性。变量存储存储媒介,如存储器、外存储器和CPU通用存储器。变量属性数据类型存储特性第54页第54页变量(按存在时间)静态存储变量(static)动态存储变量(auto)生存期为程序执行整个过程,在该
29、过程中占有固定存储空间,也称永久存储。只生存在某一段时间内函数形参、函数体或分程序中定义变量,只有当程序进入该函数或分程序时才分派存储空间,函数/分程序执行完后,变量存储空间又被释放。第55页第55页C语言变量存储特性可分四类:自动型(auto)、静态型(static)、外部型(extern)和存储器型(register)。 完整变量定义:存储特性 数据类型 变量名;自动型变量auto 类型标识符 变量列表;经常能够缺省auto。函数内定义局部变量、函数形式参数和函数体内分程序局部变量。随函数调用而存在,随函数返回而消失,它们在一次调用结束到下一次调用开始之间不再占有存储空间。作用域局限于所定
30、义函数,其生存期就是函数生存期,在一个函数中引用另一个函数自动型变量值是错误。第56页第56页外部型变量“外部型变量”,是相对于在函数“内部”阐明变量而言,它们在函数外部定义。外部型变量又称为全局变量,在编译时分派存储单元。若变量定义与引用分别处于不同源文件中,即将变量作用域延伸到其它源文件,则需要在引用该变量源文件中对它进行申明,源文件或源文件中函数才干引用它。 若变量定义与引用处于同一源文件(即全局变量),在该源文件不同函数中能够经过变量名对这种变量进行直接引用,作用域在整个源文件。外部型变量申明形式:extern 类型标识符 变量名;在引用外部型变量文献中不会为其分派内存空间。 外部型变
31、量生存期为整个程序运营期。第57页第57页void main() /*在函数main中引用变量a不必申明*/extern int a; /*申明变量a为外部型变量*/void fac(int n) a=a*n;/*函数fac外已申明a为外部变量,函数fac能够引用a*/ int a=1; /* 在s.c文献定义了全局变量a */void fun() /*在函数fun中引用变量a不必申明*/ /*源文献s.c*/*源文献d.c*/比如:第58页第58页静态型变量静态型变量是指在编译时分派存储空间变量。static 类型标识符 变量列表;局部静态变量全局静态变量 函数内部定义静态变量 作用域与自动
32、型变量相同 当所在函数执行结束后,静态变量所占内存单元并不释放,其值仍然保留 函数外部定义静态变量 在程序中凡未指明存储类型全局变量均为全局静态型变量 作用域仅限所在源程序文献 第59页第59页int f(int a) int b=0; static int c=3; b+;c+; printf( %5d%5d%5d ,a,b,c); return(a+b+c);void main() int a=2,k; for(k=0;k3;k+) printf(%5dn,f(a);课堂练习3:分析下面程序运营结果。运营结果为:1 4 72 1 5 82 1 6 9第60页第60页存储器型变量变量(按使用
33、 存储媒介)内存变量存储器变量使用CPU中存储器来存取变 量值前面所简介变量都为内存变量由编译程序在内存中分派存储单元变量存取在内存储器中完毕。存储器处于CPU内部 可避免CPU频繁访问存储器, 从而提升程序执行速度。 数据内存 运算器 运算器 结果 控制器 数据存储器第61页第61页存储器型变量不能定义为全局变量只限于整型、字符型和指针型局部变量。不能够定义为静态局部变量。只能为动态变量普通只允许同时定义两个存储器变量。register 类型名 变量名;存储器型变量定义形式: register int a; register char b; 第62页第62页课堂练习4:阅读程序,写出运营结果
34、:void main() int i=1; static int a=10; register int b=5; printf(” i=%d,a=%d,b=%dn”,i,a,b); other(); printf(” i=%d,a=%d,b=%dn ”,i,a,b);other() int i; static int a; i=6; a=100; printf(” i=%d,a=%dn ”,i,a);i=1,a=10,b=5i=6,a=100i=1,a=10,b=5第63页第63页9.7局部变量和全局变量9.7.1局部变量在一个函数内部定义变量是内部变量,它只在本函数范围内有效,也就是说只有在
35、本函数内才干使用它们,在此函数以外是不能使用这些变量。这称为“局部变量”。 第64页第64页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有效 第65页第65页(1) 主函数中定义变量(m,n)也只在主函数中有效,而不由于在主函数中定义而在整个文献或程序中有效。主函数也不能使用其它函数中定义变量。(2) 不同函数中能够使用相同名字变量,它们代表不同对象,互不干扰。比如, 上面在f1函数中定义了变
36、量b和c,倘若在f2函数中也定义变量b和c,它们在内存中占不同单元,互不混同。(3) 形式参数也是局部变量。比如上面f1函数中形参a,也只在f1函数中有效。其它函数能够调用f1函数,但不能引用f1函数形参a。(4) 在一个函数内部,能够在复合语句中定义变量,这些变量只在本复合语句中有效,这种复合语句也称为“分程序”或“程序块”。说明第66页第66页void main ( )int a,b;int c; c=a+b; c在此范围内有效 a,b在此范围内有效 第67页第67页9.7.2 全局变量在函数内定义变量是局部变量,而在函数之外定义变量称为外部变量,外部变量是全局变量(也称全程变量)。全局变
37、量可认为本文献中其他函数所共用。它有效范围为从定义变量位置开始到本源文献结束。 第68页第68页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; 第69页第69页例 9.15 有一个一维数组,内放个学生成绩,写一个函数,求出平均分、最高分和最低分。 #include float Max,Mi
38、n; *全局变量*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“,Max,Min,ave); 第70页第70页float average(float array ,int n) * 定义函数,形参为数组 */ int ; float aver,sum=array; Max=Min=array; for(=;) if(arrayMax)Maxar
39、ray; else if(arrayMin)Min array; sum=sum+array; aver; return(); 运营情况下列: 第71页第71页第72页第72页提议不在必要时不要使用全局变量,原因下列: 全局变量在程序所有执行过程中都占用存储单元,而不是仅在需要时才开辟单元。 使用全局变量过多,会减少程序清楚性,人们往往难以清楚地判断出每个瞬时各个外部变量值。在各个函数执行时都也许改变外部变量值,程序容易犯错。因此,要限制使用全局变量。第73页第73页它使函数通用性减少了,由于函数在执行时要依赖于其所在外部变量。假如将一个函数移到另一个文献中,还要将相关外部变量及其值一起移过去
40、。但若该外部变量与其它文献变量同名时,就会出现问题,减少了程序可靠性和通用性。普通要求把程序中函数做成一个封闭体,除了能够通过“实参形参”渠道与外界发生联系外,没有其它渠道。第74页第74页例 9.6 外部变量与局部变量同名#include 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=ab?ab; 形参a、b作用范围 return (
41、c); 运营结果为8 第75页第75页9.7 变量存储类别 9.7. 动态存储方式与静态存储方式 前面已简介了从变量作用域(即从空间)角度来分,能够分为全局变量和局部变量。那么从变量值存在时间(即生存期)角度来分,又能够分为静态存储方式和动态存储方式。 所谓静态存储方式是指在程序运营期间由系统分派固定存储空间方式。而动态存储方式则是在程序运营期间依据需要进行动态分派存储空间方式。这个存储空间能够分为三部分: 程序区静态存储区动态存储区第76页第76页在语言中每一个变量和函数有两个属性:数据类型和数据存储类别。对数据类型,读者已熟悉(如整型、字符型等)。存储类别指是数据在内存中存储方式。存储方式
42、分为两大类:静态存储类和动态存储类。详细包括四种:自动(),静态(),存储器(),外部()。依据变量存储类别,能够知道变量作用域和生存期。 第77页第77页9.7. auto变量函数中局部变量,如不专门申明为static存储类别,都是动态地分派存储空间,数据存储在动态存储区中。函数中形参和在函数中定义变量(包括在复合语句中定义变量),都属这类,在调用该函数时系统会给它们分派存储空间,在函数调用结束时就自动释放这些存储空间。因此这类局部变量称为自动变量。自动变量用关键字作存储类别申明。比如:int (int ) *定义f函数,为形参 *auto int ,; *定义、为自动变量 * 第78页第7
43、8页9.7.3用static申明局部变量 有时希望函数中局部变量值在函数调用结束后不消失而保留原值,即其占用存储单元不释放,在下一次该函数调用时,该变量已有值,就是上一次函数调用结束时值。这时就应当指定该局部变量为“静态局部变量”,用关键字进行申明。通过下面简朴例子能够理解它特点。第79页第79页例97 考察静态局部变量值。#include void main()int (int); ,; (; ( ,(); int (int ) int ; ; ; (); 第80页第80页对静态局部变量阐明:(1) 静态局部变量属于静态存储类别,在静态存储区内分派存储单元。在程序整个运营期间都不释放。而自动
44、变量(即动态局部变量)属于动态存储类别,占动态存储区空间而不占静态存储区空间,函数调用结束后即释放。(2) 对静态局部变量是在编译时赋初值,即只赋初值一次,在程序运营时它已有初值。以后每次调用函数时不再重新赋初值而只是保留上次函数调用结束时值。而对自动变量赋初值,不是在编译时进行,而是在函数调用时进行,每调用一次函数重新给一次初值,相称于执行一次赋值语句。第81页第81页(3)如在定义局部变量时不赋初值话,则对静态局部变量来说,编译时自动赋初值(对数值型变量)或空字符(对字符变量)。而对自动变量来说,假如不赋初值则它值是一个不拟定值。这是由于每次函数调用结束后存储单元已释放,下次调用时又重新另
45、分派存储单元,而所分派单元中值是不拟定。(4) 即使静态局部变量在函数调用结束后仍然存在,但其它函数是不能引用它。第82页第82页例98 输出到阶乘值。#include void main()int fac(int ); int ; for(;) printf(%!=,fac();Int fac(int )static int ; *; (); 第83页第83页9.7.4 register变量 普通情况下,变量(包括静态存储方式和动态存储方式)值是存储在内存中。当程序中用到哪一个变量值时,由控制器发出指令将内存中该变量值送到运算器中。 通过运算器进行运算,假如需要存数,再从运算器将数据送到内存
46、存储。 第84页第84页假如有一些变量使用频繁(比如在一个函数中执行次循环,每次循环中都要引用某局部变量),则为存取变量值要花费不少时间。为提升执行效率,语言允许将局部变量值放在CPU中存储器中,需要用时直接从存储器取出参与运算,不必再到内存中去存取。由于对存储器存取速度远高于对内存存取速度,因此这样做能够提升执行效率。这种变量叫做存储器变量,用关键字作申明。比如,例819中程序是输出到n阶乘值。第85页第85页例919使用存储器变量#include void main ( )long fac(long); long i,n; scanf(%ld,&n); for(i=1;i=n;i+) pr
47、intf(%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);第86页第86页.7.5用extern申明外部变量外部变量是在函数外部定义全局变量,它作用域是从变量定义处开始,到本程序文献末尾。在此作用域内,全局变量可认为程序中各个函数所引用。编译时将外部变量分派在静态存储区。有时需要用extern来声明外部变量,以扩展外部变量作用城。第87页第87页1. 在一个文献内申明外部变量例9.20 用extern申明外部变量,扩展它在程序文献中作用域
48、。#include void main() int max(int,int); *外部变量申明* extern A,B; printf(%dn,max(A,B); int A=13,B=-8; *定义外部变量* int max(int x,int y) *定义函数 * int z; z=xy?x:y; return(z); 第88页第88页2. 在多文献程序中申明外部变量例9.21 用extern将外部变量作用域扩展到其它文献。 本程序作用是给定值,输入和,求和am值。文献file中内容为:#include int A; /*定义外部变量*/void main() int (int); /*函
49、数申明*/ int ,; printf(enter the number a and its power m:n); scanf(,A,); A*; printf(*,A,); (); printf(*n,A,); 第89页第89页文献file中内容为:extern A; /*申明A为一个已定义外部变量*/ int (int );int ,; for(;) *A; (); 第90页第90页9.7.6用static申明外部变量有时在程序设计中希望一些外部变量只限于被本文献引用,而不能被其它文献引用。这时能够在定义外部变量时加一个申明。比如:file1.c file2.cstatic int A;
50、 extern int A;void main ( ) void fun (int n) A=A*n; 第91页第91页9.7.7关于变量申明和定义对变量而言,申明与定义关系稍微复杂一些。在申明部分出现变量有两种情况:一个是需要建立存储空间(如:int a; ),另一个是不需要建立存储空间(如:extern a;)。前者称为“定义性申明”(defining declaration) ,或简称定义(definition)。 后者称为“引用性申明”(referencing declaration)。广义地说,申明包括定义,但并非所有申明都是定义。对“int a;” 而言,它既是申明,又是定义。而对
51、“extern a;” 而言,它是申明而不是定义。第92页第92页普通为了叙述以便,把建立存储空间申明称定义,而把不需要建立存储空间申明称为申明。显然这里指申明是狭义,即非定义性申明。比如:void main() extern A; /*是申明不是定义。申明A是一 个已定义外部变量*/ int A; 第93页第93页9.7.8存储类别小结(1) 从作用域角度分,有局部变量和全局变量。它们采用存储类别下列:局部变量 |自动变量,即动态局部变量 (离开函数,值就消失) |静态局部变量(离开函数,值仍保留) |存储器变量(离开函数,值就消失) |(形式参数能够定义为自动变量或存储 器变量)全局变量
52、|静态外部变量(只限本文献引用) |外部变量 (即非静态外部变量,允许其它文献引用)第94页第94页(2) 从变量存在时间(生存期)来区别,有动态存储和静态存储两种类型。静态存储是程序整个运营时间都存在,而动态存储则是在调用函数时暂时分派单元。动态存储 |自动变量(本函数内有效) |存储器变量(本函数内有效) |形式参数(本函数内有效)静态存储 |静态局部变量(函数内有效) |静态外部变量(本文献内有效) |外部变量(其它文献可引用)第95页第95页(3) 从变量值存储位置来区别,可分为:内存中静态存储区 |静态局部变量 |静态外部变量(函数外部静态变量) |外部变量(可为其它文献引用)内存中
53、动态存储区:自动变量和形式参数CPU中存储器:存储器变量第96页第96页() 关于作用域和生存期概念。从前面叙述能够知道,对一个变量性质能够从两个方面分析,一是变量作用域,一是变量值存在时间长短,即生存期。前者是从空间角度,后者是从时间角度。两者有联系但不是同一回事。 (5) static对局部变量和全局变量作用不同。对局部变量来说,它使变量由动态存放方式改变为静态存放方式。而对全局变量来说,它使变量局部化(局部于本文件),但仍为静态存放方式。从作用域角度看,凡有static申明,其作用域都是局限,或者是局限于本函数内(静态局部变量),或者局限于本文件内(静态外部变量)。 第97页第97页9.
54、8 内部函数和外部函数 函数本身在一个文献中为全局。即一个文献中定义函数可被该文献所有其它函数调用。由于一个函数要被另外函数调用。 但是,也能够指定函数不能被其它文献调用。依据函数能否被其它源文献调用,将函数区别为内部函数和外部函数9.8.1 内部函数只能被本文献中其它函数调用 假如一个函数只能被本文献中其它函数所调用,它称为内部函数。在定义内部函数时,在函数名和函数类型前面加static。即static 类型标识符 函数名(形参表)如 static int fun ( int a , int b )第98页第98页例: static int max (a, b) int a, b; 则该函数
55、max只能被本文献中其它函数引用,而不能被其它文献中函数引用。第99页第99页 既可被本文献中函数调用,也可被其它文献中函数调用。9.8.2 外部函数(1) 在定义函数时,如果在函数首部最左端加关键字extern,则表示此函数是外部函数,可供其他文献调用。如函数首部可以写为extern int fun (int a, int b)这样,函数fun就可认为其他文献调用。C语言规定,如果在定义函数时省略extern,则隐含为外部函数。本书前面所用函数都是外部函数。第100页第100页(2) 在需要调用此函数文献中,用extern对函数作申明,表示该函数是在其它文献中定义外部函数 第101页第101
56、页【例9.5】在一个字符串内删除指定字符(用外部函数实现)。 file1.c(文献1)main ( ) extern enter_string ( ); delete_string( ); extern print_string( ); /* 阐明本文献要用到其它文献中函数*/ char c; static char str80; enter_string (str); scanf (“ %c”, &c); delete_string (str, c); print_string (str);第102页第102页 file2.c (文献2)#include stdio.h extern ent
57、er_string (str) /*定义外部函数enter_string*/char str80; gets (str); /* 读入字符串str */第103页第103页 file3.c (文献3)extern delete_string (str, ch) /* 定义外部函数delete_string */char str , ch; int i, j; for (i=j=0; stri!= 0 ; i+) if (stri!=ch) strj+=stri; stri= 0 ; 第104页第104页 file4.c (文献4)extern print_string (str) /* 定义外
58、部函数print_string */char str ; printf(%s, str);运营情况下列:abcdefgcc abdefg第105页第105页实现办法:在工程中加入相关C文献: 添加file2.c,file3.c,file4.c(如右图)或者: 在 main ( )前面加上: #include file2.c #include file3.c #include file4.c第106页第106页9.9 程序举例【例9.6】用递归法将任意十进制整数转换成R进制数(R在216之间)。程序分析: 将十进制整数n转换成R进制数基本办法是用n重复清除R,将所得余数逆序输出即可。能够定义变量
59、R和i,全局数组a用来存储十进制数n除以R后余数,由于要转换非进制数范围是216,因此定义了全局数组str。通过若干次递归调用后,a数组中存储元素是n每次除以R后产生余数。在主函数中利用while循环将a数组中元素逆序输出。第107页第107页程序代码下列:#include stdio.h#include conio.hint R;int a16=0;char str17=0123456789ABCDEF;int i;void main() void notdecimal(int n); int n; printf(please input an integer number:); scanf
60、(%d,&n); 第108页第108页 if(n0) printf(%c,strai); printf(n);void notdecimal(int n) if(n!=0) ai+=n%R; notdecimal(n/R); 程序运营情况下列:please input an integer number:123please input R:21111011第109页第109页【例9.7】验证任一偶数(不小于4)均可分解为两个素数之和,编程求出所有这样素数对。程序分析 本例主要是素数判断问题。首先要找出从键盘任意输入大偶数m从3m/2之内所有素数i,然后再判断m-i是否为素数,若是则输出素数对。
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 工程建筑劳务分包协议范本
- 合同签订的法律依据解析3篇
- 合同条款修改协议3篇
- 关于调解协议书范文集合3篇
- 住宅小区土方供应3篇
- 弱电监控系统建设项目招标3篇
- 空调器个性化定制服务考核试卷
- 木材标准化尺寸与加工适应性考核试卷
- 竹材采运企业社会责任与公益事业考核试卷
- 美容仪器产品的市场潜力评估与分析考核试卷
- 啤酒采购合同协议书模板
- 中医把脉入门培训课件
- 高血糖症的急救与护理
- 成人失禁性皮炎的预防与护理
- 技术信息收集与分析方法考核试卷
- 小学2025年国防教育课程开发计划
- 2025届安徽省示范高中皖北协作区高三下学期一模考试英语试题(原卷版+解析版)
- 防溺水家长测试题及答案
- 义务教育数学课程标准(2024年版)
- 三年级下册面积单位换算练习100道及答案
- 住宅项目开盘前工作倒排表
评论
0/150
提交评论