《模块化程序设计》PPT课件.ppt_第1页
《模块化程序设计》PPT课件.ppt_第2页
《模块化程序设计》PPT课件.ppt_第3页
《模块化程序设计》PPT课件.ppt_第4页
《模块化程序设计》PPT课件.ppt_第5页
已阅读5页,还剩140页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、第四章 模块化程序设计,主讲人:韩维良 2020年8月27日,C语言程序设计,C语言支持模块化程序设计的功能: (1) C语言用函数组织程序: 一个程序由一个或多个源程序文件组成; 每一个源程序文件就是一个程序模块; 每一个程序模块由一个或多个函数组成。 程序设计的任务就是设计一个个函数,并且确定它们之间的调用关系。 在设计函数时要使每个函数都具有各自独立的功能和明显的界面。,C语言程序设计,(2) 通过给变量定义不同的存储类别,控制模块内部及外部的信息交换。 (3) 具有编译预处理功能,为程序的调试、移植提供了方便,也支持了模块化程序设计。,C语言程序设计,第一节 函 数 一、设计C语言程序

2、就是设计函数 用C语言设计程序,任务只有一种,就是编写函数,至少也要编写一个main函数。 执行C程序也就是执行相应的main函数。即从main函数的第一个前花括号开始,依次执行后面的语句,直到最后的后花括号为止。,C语言程序设计,模块化程序设计有一个原则:就是每个模块的规模一般不能太大(一般要控制在4060行之间),以便于阅读,便于检查其中的错误。 在C语言中,减少主函数规模的一项基本措施就是通过调用其它函数来实现主函数需要的一些功能。,一般C语言程序的执行过程,程序结构图,大学信息管理系统,菜单函数,C语言程序设计,二、函数结构 一个C语言函数的结构形式如下: 函数头 函数体 ,C语言程序

3、设计,1. 函数头 函数头结构: 函数类型 函数名(形式参数表列) 一个函数的函数头的结构如下: (1)函数类型:指定函数值的类型,即函数返回值的类型。 (2)函数名:必须采用合法的用户标识符。 (3)圆括号:是“函数运算符”,表示进行函数运算,函数运算符具有很高的运算优先级别。,C语言程序设计,(4)形式参数表:由写在一对圆括号(函数运算符)中的一系列参数组成。 每一个参数由一个类型符和一个参数名组成。参数名是合法的标识符。 函数可以没有参数,这时在函数运算符内写一个“void”,也可以空白,但小括号不能省略。,C语言程序设计,2. 函数体 函数体由一些语句组成。 主要是三种类型的语句: 声

4、明语句:声明在函数中要使用的变量等程序实体。 可执行语句:用来实现函数的功能,包括若干流程控制 语句和表达式语句。 return语句:停止本函数的执行,使流程返回到调用处。,C语言程序设计,编译器将函数分为三类进行处理: 纯粹计算型函数,如sqrt()和sin()。将返回一个计算结果。 完成一项具体工作,返回完成的成败。如printf()执行成功时,返回显示的字节数;失败时,返回一个负整数。 函数不产生返回值,其类型应定义为void。 C99规定,对于非void类型的函数,必须使用有返回值的return语句。,int absolutevalue (int x) /* 绝对值函数 */ retu

5、rn (x=0?x:-x); void spc (int n) /* n空格函数 */ int i; for (i=0; in;i+) printf (); return; /* 可以不要return语句 */ ,float y(float x) if (x0) return (x*x-x+1); else return (x*x*x-x+3); ,func(int n) if (n10) return (2*n+3); else return; ,平行的两个返回语句,返回值不同,编译时可能出现警告错误,但不影响使用,结果不可预测。,C语言程序设计,3. 函数中变量的作用域 作用域指的是一个程

6、序段中的变量的作用范围,在一个函数中定义的变量只在本函数中有效,在其它函数中不能使用这个变量。 因此说,该变量的作用域是它所在的函数(从定义该变量的行开始到函数末尾)。即使在不同的函数中定义了同名的变量,它们也是指不同的变量。,#include int func(int x) x=5; return x+3; int main(void) printf(x=%dn,x); return 0; ,这个程序企图在main函数中使用func函数中的变量x。编译这个程序,系统将给出如下编译错误: c(12) : error C2065: x : undeclared identifier (未声明的标

7、识符 ),C语言程序设计,4. 空函数 空函数是一个不产生任何有效操作的函数,但它却是一个合法的C函数。 例如函数 void null(void) 就是一个空函数。 空函数多使用在模块化程序的设计或测试中。有一些函数还未编写,可用空函数进行调试,这样就可以调试程序的其它部分,等以后再逐步补上。,C语言程序设计,三、函数定义与函数声明 1、函数定义 函数定义是按照C语言的语法规则引入新的函数,并提供如下信息: 函数的返回值类型(如果有); 参数的个数及类型和名称; 调用函数时要执行的代码; 函数的有效性(不能嵌套定义)。,C语言程序设计,函数的定义 类型说明符 函数名(类型名 形式参数1 , 类

8、型名 形式参数2 , ) /* 函数的首部 */ 说明部分 /* 函数体 */ 语句部分 ,C语言程序设计,其中类型说明符和函数名部分共称为函数头。 类型说明符即本函数的类型,实为函数返回值的类型。 函数名是由用户定义的标识符,函数名后有一个括号,其内容为形参表。 在形参表中给出的参数称为形式参数,即形式参数变量的定义,各参数定义之间用逗号间隔。在进行函数调用时,主调函数将赋予这些形式参数实际的值。 当形参表中无参数时,括号不可少。 中的内容称为函数体。 在函数体中也有类型说明, 这是对函数体内部所用到的变量的类型说明。,注意:函数不能嵌套定义。 void fun1() int fun2()

9、,而应该为如下形式: void fun1() int fun2() ,C语言程序设计,2、函数的返回值及其类型 (1) return语句 函数的返回值通过函数体内的return语句实现。 return 返回值表达式 ; 或 return (返回值表达式); 如果没有返回值,格式中的左、右圆括号可以省略,即写为: return ; 功能:返回调用函数,并将“返回值表达式”的值带给调用函数。,C语言程序设计,说明: 1)函数的返回值只能有一个。 2)当函数中不需要指明返回值时,可以写成: return; 也可以不写。函数运行到右花括号自然结束。 3)一个函数体内可以有多个返回语句,不论执行到哪一个

10、,函数都结束,回到主调函数。 4)当函数没有指明返回值,即return; 或没有返回语句时,函数执行后实际上不是没有返回值,而是返回一个不确定的值,有可能给程序带来某种意外的影响。,C语言程序设计,(2)函数返回值的类型 函数定义时的类型就是函数返回值的类型。从理论上,C语言要求函数定义的类型应当与返回语句中表达式的类型保持一致。当两者不一致时,系统自动进行转换,将函数返回语句中表达式的类型转换为函数定义时的类型。 如果定义函数缺省函数类型,则系统一律按int型处理。,C语言程序设计,3、函数声明 函数声明是对所用到的函数的特征进行必要的声明。 编译系统以函数声明为检测依据:检测形参与实参类型

11、是否一致,返回值的类型是否正确,以保证调用表达式与函数之间的参数正确传递。 声明语句提供的必要信息包括:函数名,函数的类型,函数参数的个数、排列次序以及每个参数的类型。,C语言程序设计,函数的定义可以按任意的次序,但在主调函数中,要调用另一个函数,则需要对被调函数先做声明(或者说明)。 所谓声明,就是把函数的类型、函数的名称、函数形参的类型及个数、顺序通知编译系统,便于在调用时进行检查。 函数声明的一般形式: 函数类型 函数名(参数类型1 , 参数类型2) ; 或 函数类型 函数名(参数类型1 参数名1 , 参数类型2 参数名2) ;,C语言程序设计,说明: (1)如果被调函数的返回值为int

12、类型,则不管被调函数位置如何均不需要在主调函数中说明。 (2) 如果被调用函数的声明放在源文件的开头,则该声明对整个源文件都有效。 (3) 如果被调用函数的声明是在调用函数定义的内部,则该声明仅对该调用函数有效。,C语言程序设计,(4) 在函数调用中,有下面几种情况可以省略声明: a. 如果调用函数和被调用函数是在同一个源文件中,而且被调用函数的定义是在调用函数之前。 b. 函数的返回值是整数或字符。 c. 所有被调用函数的声明都是在源文件开头。 d. 如果被调用函数不是在源文件开头,而是在源文件中间且在所有函数之外某处被声明时,则在被声明之后引用它时,不必再声明,但在被声明之前引用它时,需要

13、对其进行声明。 (5) 如果调用函数和被调用函数的定义是在同一源文件中,应该先说明或定义被调用函数。,设有一函数的定义为: double func (double a, int b, char c) /* 函数体*/ 与之相应的函数声明应为: double func (double x, int y, char z); /* 注意末尾的分号 */,33,C语言程序设计,例求二实数之和 main ( ) float add(float,float); float a, b, c; scanf(%f%f, 说明:调用过程中参数的对应关系。,C语言程序设计,例 定义一个求两个整数中极大值的max()

14、函数。 /*功能:求2个整数中的较大数 */ /*形参:2个,整型 */ /*返回值: 2个整数中的较大数 */ int max(int n1,int n2) /*函数定义 */ int z; z= n1 n2 ? n1 : n2; return (z); ,C语言程序设计,void main( void ) int max(int n1, int n2); /*对被调函数说明*/ int num1,num2; printf(input two numbers: ); scanf(%d%d, printf (%f+%f=%fn, x,y, add(x,y); float add (unsign

15、ed int a, unsigned int b) printf(a=%u, b=%un, a,b); return (a+b); ,分析结果为什么错误? 如何改正?,3、传值调用的虚实结合过程 程序进行编译时,并不为形式参数分配存储空间。 只有在被调用时,形式参数才临时地占有存储空间,其过程如下: (1)调用开始,系统为形参开辟一个临时存储区,形参与实参各占一个独立的存储空间。 (2)将各实参之值传递给对应形参,形参得到了实参的值。这种虚实结合方式称为“值结合”。,(3)函数返回时,临时存储区也被撤销。 特别注意:在C程序中实参与形参是传值调用(call by value) 的特点。即实参与

16、形参是“单向的值传递” 即函数中对形参变量的操作不会影响到调用函数中的实参变量,即形参值不能传回给实参。,#include void swap (int x, int y); int main(void) int a=3, b=5; swap (a,b); printf (a=%d, b=%dn, a,b); return 0; void swap (int x, int y) int temp; temp=x, x=y, y=temp; /* 交换变量的值 */ printf(x=%d,y=%dn,x,y); ,执行结果: x=5,y=3 a=3,b=5,企图交换变量a,b的值,没有成功,五

17、、函数的嵌套调用,语言的函数定义是互相平行、独立的 函数不能嵌套定义 可以嵌套调用函数 调用一个函数的过程中,又可以调用另一个函数。,函数的嵌套调用的过程,main函数,调用a函数,结束,a函数,调用b函数,b函数,C语言程序设计,例一个偶数为两个素数之和。 德国数学家哥德巴赫(GoLdbach)在1725年写给欧拉(Euler)的信中写下了以下猜想:任何不小于6的偶数都是两个素数之和(俗称为1+1)。如633,1257等等。 两个多世纪过去了,这一猜想既无法证明也没被推翻。试设计程序验证这一猜想是否成立。 对于一个偶数,找出一个小于它的数,判断是否是素数,如果是就计算该偶数与该素数的差并判断

18、它是否是素数。如果两者都是素数则猜想得到验证。 问题表现为:任意输入一个偶数,可将其分解为两个素数之和。,50,C语言程序设计,#include main() void even(int x); int a; printf(“请输入一个偶数:”); scanf(“%d”, ,51,C语言程序设计,void even(int x) int isprime(int a); int i; for(i=2;i=x/2;i+) if ( isprime(i) ,C语言程序设计,int isprime(int a) int i; for(i=2;i=a/2;i+) if(a%i=0) return 0;

19、return 1; 注意两个返回语句。,C语言程序设计,例输入一个由若干个单词组成的文本串,每个单词之间用空格、换行符、跳格符(制表符)分隔,且分隔符可以不至一个(如连续的两个空格),以作为输入的结束标志,统计此文本串单词的个数? 如输入:This is a student. 分析:因为计算机无法直接识别单词,所以从输入的文本串中逐个读取每一个字符,当读到每一个单词的第一个字母时,计数器加1 。 如何知道读取到的字符是单词的第一个字母呢? 如果读取到的字符是分隔符,则不是单词的一部分; 如果读取到的字符不是分隔符,且它的上一个字符是分隔符,则它是单词的第一个字母,计数器加1。 如果读取到的字符

20、不是分隔符,且它的上一个字符也不是分隔符,则它是单词的非首字母,计数器不加1。,C语言程序设计,当前读取到的字符是否是单词的一部分,可用一个状态变量来表示,如果是单词的一部分,则用1表示,反之用0表示。 #include #define IN 1 #define OUT 0 int countword(); main() int n; n=countword(); printf(“单词数为:%dn”,n); ,55,C语言程序设计,int countword() /*没有形参*/ int c,nw,state; state=OUT; nw=0; while(c=getchar()!=) if(

21、c= |c=n|c=t) state=OUT; else if(state=OUT) state=IN; nw+; return(nw); ,六、递归函数 一个函数可以调用另一个函数。 递归函数一个函数自己调用自己(直接地或间接地调用自己)。 前者称为直接递归调用函数,后者称为间接递归调用函数。 1.递归调用次数有限 2.程序执行有规律 3.有结束调用的条件,C语言程序设计,递归调用是指一个函数直接或间接地调用自己。这种函数称为递归函数。自己调用自己,即要解决一个问题,转化为一个用同样的方法解决的新问题,只是参数在不断地递增或者递减。 在递归调用中,主调函数又是被调函数。执行递归函数将反复调用

22、其自身。 对于一些特定的问题,用递归算法来解决,可以简化问题的复杂程度。,8/27/2020,58,8/27/2020,59,C语言程序设计,例:用递归调用求n!。 要用递归法解决此问题,必须明确两个问题: 、递归算法 n!=n*(n-1)! (n-1)!=(n-1)*(n-2)!; 求n的阶乘的问题,转化为用同样的方法求(n-1)的阶乘。 如:4!4*3! 3!3*2! 如果编写成一个函数,则调用如下: f(n)=n*f(n-1); 不断地自己调用自己。,C语言程序设计,、递归终结条件 递归到一定的程度后,必须终止。 如求阶乘时,当求到1!时,必须停止递归。 f(1)=1 这一条是必须的,否

23、则将会没完没了的进行下去。 通常用条件判断, 满足某种条件后就不再作递归调用,然后逐层返回。,C语言程序设计,例如有函数f如下: int f(int x) z=f(x1); return z; 上页中的函数是一个递归函数。 但是运行该函数将无休止地调用其自身,这当然是不正确的。为了防止递归调用无终止地进行, 必须在函数内有终止递归调用的手段。,C语言程序设计,例 用递归法计算n!。 #include long f(int n) long y; if(n1) y=n*f(n-1); else y=1; return(y); ,64,C语言程序设计,main() int n; long y; pr

24、intf(“input a inteager number:”); scanf(%d, 程序运行情况如下: input a inteager number: 5 5!=120,C语言程序设计,程序说明: 程序中给出的函数f是一个递归函数。主函数调用f 后即进入函数f执行,如果n0,n=0或n=1时都将结束函数的执行,否则就递归调用f函数自身。由于每次递归调用的实参为n-1,即把n-1 的值赋予形参n,最后当n-1的值为1时再作递归调用,形参n的值也为1,将使递归终止。然后可逐层退回。,C语言程序设计,如求4!的过程分解如下:,C语言程序设计,例:用递归法求菲波那契数列第40项的值,该数列为:1

25、,1,2,3,5,8,13,21, 规律:每一项为其前两项的和。 递归算法如下: fib(n)=fib(n-1)+fib(n-2); 递归的终结条件如下: fib(1)=1; fib(2)=1;,C语言程序设计,#include main() int n; long fib(int); /*第40项的值很大*/ printf(“请输入项数:”); scanf(“%d”, ,69,C语言程序设计,long fib(int n) long result; if(n=1|n=2) result=1; else result=fib(n-1)+fib(n-2); return result; ,第二节

26、 变量的存储属性 变量是对程序中数据存储的抽象。 C语言程序中变量都有类型,数据类型是变量的运算属性的抽象,决定了该变量的取值范围和可以施加的运算种类。 除此之外,变量还有一些属性,例如: 一个变量在程序的哪个范围内是可以使用的变量的可用域; 它什么时候生成以及什么时候被撤消变量的生存期;,它存储在什么哪种类型的存储器中以及用什么机制进行存储变量的存储区。 这些都称为变量的存储属性。,一、变量的作用域与生存期 在函数中形参变量只在被调用期间才分配内存单元,调用结束立即释放。形参变量只有在函数内才是有效的,离开该函数就不能再使用了。 如:货币的使用范围,校园卡的使用范围。 1. 局部变量和全局变

27、量 变量的可用域是指一个变量在程序的哪个域内才是可以被识别的,也称为可见(或可用)的。 变量可分为两种: 全局可用全局变量 局部可用局部变量。,C语言程序设计,局部变量:在一个函数或者复合语句(如函数体、循环体)内部定义的变量是局部变量,它只在该函数或者复合语句范围内有效。而在其它函数内不能使用。 只有在包含变量定义的函数内部,才能使用被定义的变量,在此函数之外就不能使用这些变量了。又称为内部变量。 例如: int f1(int a) /*函数f1*/ int b,c; /*a,b,c作用域:仅限于函数f1()中*/,C语言程序设计,int f2(int x) /*函数f2*/ int y,z

28、; /*x,y,z作用域:仅限于函数f2()中*/ main() int m,n; /*m,n作用域:仅限于函数main()中*/ 由于局部变量只在一个局部范围中使用,因此在不同的函数中可以定义同名的局部变量,它们互不干扰,各自分配不同的独立存贮单元。这种特性便于进行模块化设计,不同的模块由不同的程序员设计,各模块中使用的变量名由用户自己确定,即便相同也不会彼此影响。,75,C语言程序设计,#include main() void fun1(); void fun2(); int x=1; printf(“x=%dn”,x); int x=3; printf(“x=%dn”,x); print

29、f(“x=%dn”,x); fun1(); printf(“x=%dn”,x); fun2(); printf(“x=%dn”,x); fun1(); ,C语言程序设计,fun1() int x=5; printf(“x=%dn”,x); fun2() int x=7; printf(“x=%dn”,x); 说明: 四个X分别分配了不同的存贮单元。 同一函数的两次调用分配了不同的单元。,#include int main(void) /* printf(a1=%d,a); */ int a=3; printf(a2=%d,a); int b=5; printf(b1=%d,b); /* pri

30、ntf(b2=%d,b); */ printf(a3=%d,a); return 0; ,则运行可以得到如下结果: a2=3 b1=5 a3=3,C语言程序设计,全局变量:也称为外部变量,是在函数外部定义的变量,作用域为从定义全局变量位置开始到源程序结束,是多个函数间进行数据传递的一种手段。 全局变量可以在其作用范围内,由各函数所共用。 在函数中使用全局变量,一般应作全局变量说明。 只有在函数内经过说明的全局变量才能使用。 全局变量的说明符为extern。 但在一个函数之前定义的全局变量,在该函数内使用可不再加以说明。,C语言程序设计,例如: int a,b; /*外部变量*/ void f1

31、() /*函数f1*/ float x,y; /*外部变量*/ int f2() /*函数f2*/ ,C语言程序设计,main() /*主函数*/ /*全局变量x,y作用域 全局变量a,b作用域*/ 从上例可以看出a、b、x、y 都是在函数外部定义的外部变量,都是全局变量。但x,y 定义在函数f1之后,而在f1内又无对x,y的说明,所以它们在f1内无效。 a,b定义在源程序最前面,因此在f1,f2及main内不加说明也可使用。,81,C语言程序设计,#include int a=3,b=5; main() void fun(); printf(“a=%d,b=%dn”,a,b); fun();

32、 printf(“a=%d,b=%dn”,a,b); void fun() int c; c=a;a=b;b=c; printf(“a=%d,b=%dn”,a,b); ,C语言程序设计,【例】输入长方体的长宽高l,w,h。求体积及三个面x*y,x*z,y*z的面积。 int s1,s2,s3; int vs( int a,int b,int c) int v; v=a*b*c; s1=a*b; s2=b*c; s3=a*c; return v; ,C语言程序设计,main() int v,l,w,h; printf(ninput length,width and height: ); scan

33、f(%d%d%d, 程序运行结果如下: input length,width and height:4 5 6 v=120 s1=20 s2=30 s3=24,C语言程序设计,程序说明: 本程序中定义了三个外部变量s1,s2,s3,用来存放三个面积,其作用域为整个程序。函数vs用来求正方体体积和三个面积,函数的返回值为体积v。由主函数完成长宽高的输入及结果输出。 由于语言规定函数返回值只有一个,当需要增加函数的返回数据时,用外部变量是一种很好的方式。本例中,如不使用外部变量,在主函数中就不可能取得v,s1,s2,s3四个值。而采用了外部变量,在函数vs中求得的s1,s2,s3值在main中仍然

34、有效。因此外部变量是实现函数之间数据传输的有效手段。,C语言程序设计,当在函数外定义了一个全局变量后,定义点之后的函数都可以使用该变量. 当全局变量的值在一个函数中改变后,另一个函数使用的就是改变后的值. 从定义开始到程序结束,全局变量自始至终占用存储空间. 全局变量的使用使函数间的联系加强,与程序设计”低耦合”的原则相违背,所以很少使用。,例同名局部变量 #include int main(void) int a=3; printf(a1=%d,a); int a=5; printf(a2=%d,a); return 0; ,例演示全局变量 #include void a() int a=1

35、; printf(a1=%dn,a); printf(x1=%dn,x); int x=3; * 定义外部变量 */ void b() printf(x2=%dn,x); int main(void) a(); b(); printf(x3=%dn,x); return 0; ,例演示全局变量与局部变量同名 #include int x=3; /* 定义外部变量 */ int main(void) int x=5; printf(x=%dn,x); return 0; ,2. 动态变量与静态变量 运行中的程序,在内存中被分成代码区和数据区两大部分。 数据区又分为静态存储区、自动存储区和动态分配

36、区三部分。 自动存储区是按栈组织的存储区。 除特别声明的外,局部变量通常被存放在栈区。 这些变量在进入所在的块时被创建,所在的块结束时被撤销,其值不再保留。并且,变量必须有程序员显式地进行初始化,否则它们的初始值是不确定的。,静态存储区是在程序编译时就分配的存储区。 分配在静态存储区的变量在程序开始执行时被创建并自动初始化(数值变量被初始化为0),当程序结束时才被撤销。 所以常称静态变量的生存期是永久的。 全局变量就是被分配在静态存储区的。,/* 企图交换两个变量的值*/ #include void swap(void);/* 说明函数 */ int a=3, b=5;/* 声明外部变量 */

37、 int main(void) printf (a=%d, b=%dn,a, b); swap(); printf (a=%d, b=%dn, a,b); return 0; void swap(void) int c;/* 声明自动变量*/ c=a; a=b; b=c;/* 交换变量的值 */ ,二、C语言中变量的存储类型 根据可用域和生存期,变量有4种存储类型: 局部自动类型,在函数内部用标识符auto或register声明。 静态局部类型,在函数内部,使用static声明。 静态全局类型,在函数外部,使用static声明,也称静态外部变量。 全局类型,不需要标识符声明。在函数外部直接声明

38、即可,通称外部变量。,1、auto存储类型和register存储类型 使用auto声明的变量是局部变量,编译器将其分配在自动存储区。 其格式为: auto 数据类型变量名=初值表达式,; 其中方括号表示可省略,省略auto,系统隐含认为此变量为auto。 函数的参数(形参)也是一种自动变量。,C语言程序设计,自动变量的类型说明符为auto; 语言规定,函数内凡未加存储类型说明的变量均视为自动变量,也就是说自动变量可省去说明符auto。 在前面所学的程序中定义的变量凡未加存储类型说明符的都是自动变量。,C语言程序设计,例如: int a,b,k; char c; 等价于: auto int a,

39、b,k; auto char c; ,C语言程序设计,自动变量的特点: 自动变量的作用域仅限于定义该变量的函数或复合语句内。 自动变量存贮在自动存贮区,只有在使用它,即定义该变量的函数被调用时才给它分配存储单元,开始它的生存期。函数调用结束,释放存储单元,结束生存期。因此函数调用结束之后,自动变量的值不能保留。 未初始化时,自动变量的值是不确定的。 由于自动变量的作用域和生存期都局限于定义它的函数或复合语句内,因此不同的函数中允许使用同名的变量而不会混淆。,C语言程序设计,寄存器变量的说明符是register。 存放在CPU的寄存器中,使用时,不需要访问内存,而直接从寄存器中读写, 这样可提高

40、访问效率。 特点:加快运算速度,函数运行完即消失、使用有限。 如定义一个频繁使用的变量为寄存器类型: register int i;,C语言程序设计,2、静态局部类型 类型说明符:static,属于静态存储方式; 在一个函数内部,可以用static定义一个变量,称为静态局部变量。其作用域仍在定义它的函数内部,但其生存期却程序运行期。 函数调用结束后,并不释放所占用的存贮空间(总是占用固定的存贮空间),在程序运行期间,始终存在,但除了定义它的函数以外,别的函数不能使用。整个程序运行结束后,才释放占用的空间。,C语言程序设计,静态局部变量的特点: (1)静态局部变量在每次调用函数结束后能够保留调用

41、结束后的值不变,留待下一次调用,变量值具有继承性。 (2)静态局部变量只限于被本函数使用,不能被其他函数使用 (3)静态局部变量属于静态存储类别,在整个程序运行期间都不释放;而动态存储变量属于动态存储类别,函数调用结束即释放,再次调用又将重新分配空间。 (4)在定义静态局部变量时,如果不赋初值,系统自动赋值为0或空字符(对字符型变量)。,99,C语言程序设计,#include func() static int b=10; b=b+2; return b; main() int i; for(i=1;i4;i+) printf(“%d,b=%dn”,i,func(); ,#include vo

42、id increment(void); int main(void) increment ( ); increment ( ); increment ( ); return 0; void increment(void) int x=0; /*auto*/ x+; printf (%dn,x); ,运行结果: 1 1 1,#include void increment(void); int main(void) increment ( ); increment ( ); increment ( ); return 0; void increment(void) static int x=0;

43、/*使用了static说明符 */ x+; printf (%dn,x); ,运行结果: 1 2 3,3、静态全局类型 在多文件程序中,用static声明外部变量,使该外部变量的作用域仅限于所在的文件,而不用static声明的外部变量的作用域为整个程序。 例如:某个程序中要用到大量函数,而有几个函数需要共同使用几个全局变量时,可以将这几个函数组织在一个文件中,而将这几个全局变量用static定义为静态的,以保证它们不会与其他文件中的变量发生名字冲突,保证文件的独立性。,static unsigned int r; int random(void) r=(r*123+59)%65536; ret

44、urn(r); /*产生r的初值*/ unsigned randomstart(unsigned int seed)r=seed; #include int main(void) int i, n; printf (please enter the seed:); scanf(%d, ,C语言程序设计,4、外部变量的定义与声明 外部变量的类型说明符为extern。 外部变量的特点: (1) 外部变量和全局变量是对同一类变量的两种不同角度的提法。全局变量是从它的作用域提出的,外部变量从它的存储方式提出的,表示了它的生存期。 (2)当一个源程序由若干个源文件组成时, 在一个源文件中定义的外部变量在

45、其它的源文件中也有效。 一个变量被定义点之前的函数使用或者被别的文件中的函数使用时,应用extern声明。,对外部变量(静态全局变量除外),除可以定义一次外,还可以多次进行声明(引用性声明)。 (1)通过声明将外部变量的作用域在本文件范围内扩充向前引用 通常,一个外部变量的作用域是从其定义点到本文件末尾。对于位于定义点之前的函数,可以用extern声明使变量的作用域扩充到需要用到它的函数。,#include void gx(), gy (); void main(void) extern int x,y; /*声明x,y是外部变量,作用域扩充至此*/ printf (1:x=%dt y=%dn

46、, x, y); y=246; gx(); gy(); void gx() extern int x, y; /*声明x,y是外部变量*/ x=135; printf(2:x=%dt y=%dn, x, y); int x, y; /*定义x,y是外部变量 */ void gy() printf(3:x=%dt y=%dn, x, y); ,(2)利用声明语句将作用域扩大到其他文件建立对象的外部链接 假设一个程序由两个以上的文件组成。当一个全局变量定义在文件file1中时,在另外的文件中使用extern声明,可以通知编译系统一个信息:“此变量到外部去找”。或者说在链接时,告诉链接器:“到别的文

47、件中找这个变量的定义”。,* file1.c */ #include int x, y; char ch; void main(void) x=12; y=24; f1(); printf(%cn, ch); ,* file2.c */ extern int x,y; extern char ch; f1( ) printf (%d, %dn, x,y); ch=a; ,(3) 全局变量的副作用 如果没有外部变量,函数只能通过参数与外界发生数据联系,有了外部变量以后,增加了一条与外界传递数据的渠道,但也会产生一些副作用.,/* 例能否输出5行 *?*/ #include int i; void

48、 main(void) void prt(); for(i=0; i5; i+) /* 输出5行 */ prt(); void prt() /* 输出一行5个* */ for(i=0; i5; i+) printf (%c, *); printf(n); ,三、通过const声明将变量存储在只读区 C语言可以开辟一个只读区。数据是以变量形式存储的,只读的,不可修改。也就是把变量定义为只读变量。 定义只读变量的方法是在声明变量时使用修饰符const。 格式为: const 数据类型 变量1=初始表达式1,变量2=初始表达式2, 如:const double X=3.14159265;,C语言程序

49、设计,第三节 模块的编译与链接 一、分别编译 C语言是一种支持模块化程序设计的语言,它允许将一个大型程序分成多个程序文件分别进行编译。 这样的好处在于: 当程序的一个局部有错误或对局部进行了修改时,可以只重新编译该局部,不需要将整个程序都重新编译。 某些经过考验的函数的目标代码可以添加为的库函数,供其他程序中使用。,C语言程序设计,二、用项目管理多文件程序的编译与链接过程 多文件程序的编译、链接过程是比较麻烦的。为了方便用户进行多文件程序的编译、链接,各种程序开发软件都提供了用项目(project)管理多文件程序的编译和链接过程。其使用方法因开发工具而异,请参考有关手册。,C语言程序设计,三、

50、头文件 用户显式地保证程序一致性的基本方法是保持声明的一致性。 保持声明一致性的简单办法是提供一个头文件,让头文件中的信息作为各模块之间的接口信息,有利于提供可重用的模块。 使用头文件将把程序分为程序头和程序体两部分。,好的头文件应包含如下一些内容: 类型定义 如定义一个枚举类型: enum color RED,BLUE,GREEN,YELLOW; 函数声明 如: extern int strlen const char*; 嵌入函数声明 如:inline char get() return *p+; 数据声明 如:extern int a;,常量定义 如: const float pi=3.

51、141593; 包含指令 如: #include 宏定义 如:#define case break; case 注释,好的头文件不能包含以下内容: 一般函数定义 数据定义 如: int a; 常量聚集定义 如: const tbl=/* */; 用户头文件是由用户自己定义的头文件。 系统将用双引号方式进行搜索。 #include 头文件名.h,程序员可以用它们建立各文件之间的信息窗口。 应当注意,修改了头文件时,所有涉及该头文件的程序都应重新编译,所以在编制头文件时应尽量考虑周全。 当使用很多头文件时,常有重复嵌入的情形发生,在这种情形下,应防止标识符重复定义的错误。 为避免错误,应使用条件编

52、译来测试有无重复定义。,C语言程序设计,第四节 宏定义与宏替换 编译预处理是以“#”开头的一些命令,它通知编译系统:在进行正式编译前应当先进行一些前期工作(即编译预处理)。 目前,C语言的编译预处理功能主要有如下一些: (1)文件包含,使用命令:#include。 (2)宏定义,使用命令:#define、#undef。 (3)条件编译,使用命令:#if、#ifdef、#ifndef、#else、#endif、#elif、defind。,(4)提供行号信息,使用命令:#line。 (5)编译杂注,使用命令:#progma。 (6)错误信息,使用命令:#error。 在这些编译预处理中,应用最多的

53、就是对宏的处理。 它允许程序员使用define定义并应用宏名字书写C语言程序,同时也指示编译器在正式编译前通过宏替换,使程序能按照C语言的语法进行编译。,一、字符串宏定义及其基本格式 define命令最简单的应用就是定义一个符号常量。 格式为: define 宏名 字符串 编译预处理时,预处理程序将把程序文件中在该宏定义之后的所有宏名,用#define命令中定义的字符串(称为宏体)替换,这个过程称为宏替换。 符号常数的定义就是这种宏定义的一种应用。 #define PI 3.14159265 #define RADIUS 2.0,使用宏定义的好处是: (1) 提高了程序的可读性。 如果人们看到

54、以下语句 return (2.0*3.1415926536*2.0); 和 return (3.1415926536*2.0*2.0); 是否能很快地看出它们在计算什么呢?显然不能。 如果写成下面的形式: return (2.0*PI*RADIUS); 和 return (PI*RADIUS*RADIUS); 可以看出:这是在计算圆周长和圆面积。 使用宏定义可以提高可理解性。,(2) 易修改性好。 如要将RADIUS的值由2.0修改为3.0,只要在define命令中修改一处便可。 而在不使用宏定义的文件中,要将3处的2.0修改为3.0,而且很可能也错把前一个返回语句也修改成 return (3

55、.0*3.1415926536*3.0);,(3) 宏定义可以给程序设计带来许多方便。,二、使用宏需注意的问题 (1)在#define命令中,宏名字与字符串(宏体)之间用一个或多个空格分隔。 (2)宏名字的使用 宏名字不能用引号括起来。 如:#define YES 1 printf(YES); 将不进行宏定义。,宏名中不能含有空格。 例如想用“A NAME”定义“SMISS”,而写成: #define A NAME SMISS 则实际进行的宏定义是A为宏名字,宏体是“NAME SMISS”。 C程序员一般都习惯用大写字母定义宏名字。这样的表示方法使宏名与变量名有明显的区别,以避免混淆。此外有助

56、于快速识别要发生宏替换的位置,提高程序的可读性。 不能进行宏名字的重定义。,(3)定义一个宏名字以后,就可以在后面使用这个宏名字了,包括在后面的宏定义中使用。 例如求圆的周长和面积的程序可以改写为 #include #define PI 3.1415926 #define R 1.0 #define CIRCUM 2.0*PI*R /* 使用了前面定义的R和PI */,#define AREAPI*R*R int main(void) printf(“The circum is %f and area is %fn”,CIRCUM,AREA); ,/*下面是另外一种形式的宏定义: */ #in

57、clude #define PI 3.14159265 #define RADIUS 1.0 #define CIRCUM return(2.0*PI* RADIUS); /*分号是return语句的一部分 */ #define AREA return(PI* RADIUS* RADIUS); double circum( ) CIRCUM double area( ) AREA int main(void) printf(The circum is %f and area is %fn,circum(),area(); ,(4)不能进行的宏替换。 不可以替换作为用户标识符中的成分。 例如在前

58、面的例子中,不可以用“R”替换“CIRCUM”中的“R”。 不能替换字符串常量中的成分。 举例如下:,#include #define PI 3.14159265 #define RADIUS 1.0 #define CIRCUM return(2.0*PI* RADIUS); #define AREA return(PI* RADIUS* RADIUS); int main(void) printf(“The CIRCUM is %f ”, CIRCUM); printf(“ and AREA is %fn”, AREA ); 运行结果为: The CIRCUM is 6.283185 and AREA is 3.141593不会用宏名字“CIRCUM”和“AREA”替换格式串“CIRCUM”和“AREA”。,(5)一行中写不下的宏定义,应在前一行结

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论