首页计算机计算机软件C语言程序设计 05 第4章 函数与程序结构.pptx

05 第4章 函数与程序结构.pptx

页数:103页文件大小:616.7 KB来源: C语言程序设计ppt课件

本课件从为什么引入函数机制讲起,说明模块化程序设计的特点;随后讲解库函数的使用、函数定义形式、函数调用与参数传递、嵌套与递归调用,以及数组名作函数参数的特点;最后区分局部与全局变量、动态与静态存储变量,并说明多文件程序的组织。

第4章 函数与程序结构,讲解理解函数的功能、掌握函数定义的一般形式、掌握函数的形参与实参的对应关系、参数传递方法等内容
4.5 调用函数和被调函数的数据传递,讲解函数的嵌套调用与递归调用
4.1 概述,列举函数机制与模块化程序设计、程序越来越长、重复代码增多等内容
查看本课件的文字版(共 103 页,供检索与复制)

第1页:第4章 函数与程序结构

第4章 函数与程序结构

理解函数的功能。

2.掌握函数定义的一般形式。

3.掌握函数的形参与实参的对应关系、参数传递方法及函数返回值的概念。

4.掌握函数调用的几种形式。

5.掌握函数嵌套调用的一般过程。

6.了解函数递归调用的几种形式。

7.掌握数组名作为函数的参数的一些特点。

8.掌握局部变量与全局变量的概念及其使用特点。

9.掌握动态存储变量与静态存储变量的含义,会正确识别和使用。

10.了解内部函数与外部函数的含义。

11.了解多文件程序的组织和实现方法

第2页:4.5 调用函数和被调函数的数据传递

4.5 调用函数和被调函数的数据传递

6函数的嵌套调用与递归调用4.7变量的作用域和生期4.9小结4.8多文件程序的组织和调试方法4.1概述4.2库函数4.3函数定义

第3页:4.1 概述

4.1 概述

大程序产生的问题前面的程序中除了调用库函数(如printf)实现某些功能外,所有程序功能均在主函数main中完成,当问题有一定规模和复杂度时不可避免会出现问题:

  • 函数机制与模块化程序设计
  • 程序越来越长,难于理解且可读性下降
  • 重复代码增多,某段程序可能被执行多次
  • 某一问题中的代码,无法在其他同类问题中再用
  • 程序各部分之间联系复杂、保持重用代码段的一致性等原因导致程序调试难度加大。

第4页:解决问题的方法—引入函数机制

解决问题的方法—引入函数机制

由于以上问题,所以需要一种机制,能够有效地分解复杂的描述,控制程序规模和复杂性,C语言中,这种机制就是函数(function也是功能)。

使用函数,把一段处理过程抽象出来,作为程序中独立的函数实体,这样通过函数之间的调用就可以完成复杂的处理任务了。

函数机制的本质是封装一段操作,对其他模块隐藏函数内部的实现过程,只提供调用方法,即接口参数和返回值。

  • 被抽象成函数的程序段通常是功能独立、具有独立逻辑意义的程序段和重复出现的代码段。

第5页:函数机制的提供的好处

函数机制的提供的好处

  • 函数可以被多次调用,从而减少程序的长度。
  • 增加程序的可读性。
  • 程序的模块化、结构化更强。
  • 模块化程序设计
  • 基本思想将一个大的程序按功能分割成一些小模块。
  • 开发方法自上向下,逐步分解,分而治之。4.1概述mainacbedfghijkml

第6页:模块化程序设计的特点

模块化程序设计的特点

  • 模块相对独立、功能单一、结构清晰、接口简单
  • 控制了程序设计的复杂性
  • 提高元件的可靠性
  • 避免程序开发的重复劳动
  • 缩短开发周期
  • 易于维护和功能扩充。

第7页:函数的有关概念

函数的有关概念

  • 函数名上述封装体的名称(应反映其代表的功能)。
  • 函数定义按照规定形式对函数的描述。
  • 函数调用要求执行函数的描述。
  • 库函数ANSIC标准提供的一批规范化的函数。
  • 自定义函数用户根据实际任务编写的函数。

第8页:函数的执行

函数的执行

在main中,调用库函数或自己定义的函数时,控制权转移到函数,主程序等待,待函数执行完毕后,控制返回到调用处,主函数继续执行。

  • C程序总是从主函数main开始执行,到主函数的最后一个“}”处结束。
  • 其他函数中调用函数的过程与此相同。

第9页:C语言源程序组成一个实

C语言源程序组成一个实

用的C语言源程序总是由许多函数组成,这些函数多数都是根据实际任务由用户来编写的。在这些函数中,可以调用C语言提供的库函数,也可调用用户自己或他人编写的函数。

熟悉库函数的使用与掌握函数的定义方法从C语言函数来讲,一方面要掌握库函数的调用方法,尽可能地熟悉系统提供的常用库函数的功能,尽量使用库函数实现程序功能。

另一方面,库函数毕竟不能解决所有的问题,需要掌握自己定义函数的方法。

第10页:在程序设计中对函数的两种观点

在程序设计中对函数的两种观点

从函数外部调用者的角度考虑怎样使用和设计函数,不论是库函数还是自定义函数,我们只关心函数如何使用,实现什么功能、函数名字是什么、有几个参数、类型是什么、返回什么值,而不关心函数内部的实现细节,也没有必要关心。

从这个层次上考虑问题,可以摆脱细节干扰、把握总体和全局应该具有的功能和结构。

第11页:从函数内部实现者的角度定义函数实现的功能

从函数内部实现者的角度定义函数实现的功能

要考虑函数启动时需外部提供什么数据及其类型、函数如何工作、如何得到结果、何时结束、如何返回结果,而不关心外部什么地方调用这个函数,调用时具体参数是什么。

我们在函数内部定义的计算功能是具有一般性的抽象计算功能,只有函数被调用的时候,才具体化地解决一个实际问题。

第12页:4.2 库函数

4.2 库函数

C程序所需的许多东西通过函数方式提供。掌握C语言的一个重要方面,就是要熟悉库函数的使用。

ANSIC标准将一批最常用的功能总结出来,定义了C语言的标准库、每种C语言系统,都按照这个标准提供了一批标准库函数,还根据实际运行环境提供了扩充函数库。

(参考唐书附录F)

  • 写C程序时可直接调用函数,不必关心函数是如何实现的,这样可节省大量的时间和精力。

第13页:调用库函数的两个骤步

调用库函数的两个骤步

#include<math.h>math.h是有关数学函数的头文件。
  • 使用include指出库函数的相关定义和说明
  • 使用形式:# include<头文件>(后面详解)
  • 必需以"#"开头
  • "#"开头的行不是C语句,末尾不加";"号
  • 系统提供的头文件以.h作为文件后缀,文件名用一对尖括号< >或双撇号" "括起来。
  • 调用标准库函数调用库函数的形式为:函数名(参数表)

第14页:库函数调用的两种方式

库函数调用的两种方式

表达式中调用函数是表达式的一个运算对象,通常需要使用函数的返回值进行计算;(一般是数学函数),如:y = z*sin(x)+

printf ("%d\n", a); 4.2库函数使用库函数应注意:
  • 作为独立语句可看作表达式语句;(一般是动作),如:
  • 函数功能
  • 函数参数的数目和顺序,及各参数意义和类型
  • 函数返回值意义和类型
  • 需要的包含文件。

第15页:4.3 函数定义

4.3 函数定义

一般格式函数类型函数名(类型名形参1,类型名形参2,…){说明部分语句部分}省略返回类型名时,缺省值为intC语言规定,不能在函数的内部(函数体)定义函数(即嵌套定义)。

所有函数都是互相平行独立的。

函数名、形参是用户命名的标识符,同一程序中,函数名必须唯一,形参名在同一函数中唯一即可,可与其他函数的变量同名。

第16页:函数名后括号内的参数叫形参

函数名后括号内的参数叫形参

如:有参函数(现代风格)intmax(intx,inty)//y前面的int不能省{intz;z=x>y?x:y;return(z);}

printf("*****\n"); }
  • 函数名后括号内的参数叫形参,形参必须用括号括起来,可有多个形参,也可以一个也没有。
  • 如:无参函数printstar()//或printstar(void){

第17页:函数体中说明部分的变量

函数体中说明部分的变量

只在函数体内有效,并在进入函数时为变量开辟单元,退出时释放分配的单元。

函数体中说明部分的变量与其他函数体中的变量互不相关。

如:intmax(intx,inty)//函数头部--界面{intz;//x、y、z只在max中有效z=x>y?x:y;return(z);}

函数头部构成函数内外部之间的界面。函数接口定义清楚了,函数的定义和使用完全可以由不同人来完成。

第18页:函数定义的{ }中可以为空

函数定义的{ }中可以为空

函数值通过return语句返回。

例1:写一个函数,求两个双精度浮点数的和。

doubleadd(doublea,doubleb){doubles;//s只在add中有效s=a+b;returns;//返回函数值}4.3函数定义不能省略,比较传统形式

  • 即函数什么也不做。例:空函数dummy(){}
  • 函数允许递归调用,即在函数中可直接或间接地调用自己。

第19页:在老版本C语言

在老版本C语言

中,参数类型说明允许放在函数说明部分的第2行单独指定。

其格式如下:函数类型函数名(形参表)形参类型说明{说明部分语句部分}4.3函数定义例如:求两个双精度实数的和,写成传统风格的有参函数如下:doubleadd(a,b)doublea, b;//参数说明{doubles;s=a+b;returns;}

第20页:函数的返回值

函数的返回值

  • 形式return(表达式);或return表达式;或return
  • 功能使程序控制从被调用函数返回到调用函数中,同时把返值带给调用函数。
  • 说明
  • 函数中可有多个return语句
  • 若无return语句,遇“}”时,自动返回调用函数

第21页:若函数类型与return中表达式值

若函数类型与return中表达式值

的类型不一致,按前者为准,自动转换—函数调用转换;

例2*:函数返回值类型转换intmax(x,y)floatx,y;{floatz;z=x>y?x:y;returnz;}4.3函数定义运行结果:Input a,b:3.4,

main( ){ floata, b, c; printf("Input a,b: "); scanf("%f,%f", &a,&b); c = max(a,b); printf("Max is %f\n",c); } 不一致
  • Max is5.000000

第22页:void型函数表示该函数没有值返回

void型函数表示该函数没有值返回

调用这类函数的惟一的方法就是作为独立的语句。

例:无返回值函数voidswap(x,y)intx,y{inttemp;temp=x;x=y;y=temp;}4.3函数定义例*:应作为独立语句voidprintstar(){

printf("***"); } main(){ inta=0; a=printstar(); printf("%d",a); } 

第23页:4.3 函数定义良好的程序设计

4.3 函数定义良好的程序设计

习惯:为了使程序具有良好的可读性并减少出错,凡不要求返回值的函数都应定义为空类型;即使函数类型为整型,也不使用系统的缺省处理。

第24页:4.4 函数的调用和函数说明

4.4 函数的调用和函数说明

函数语句只进行某些操作,不返回函数值。

以函数作为独立的语句,末尾一定要加“;”号。

如:而对于前面定义的函数printstar只能用函数语句调用方式:printstar();

  • 函数调用的形式函数名(实参表)
  • 函数调用方式与库函数的调用类似,有以下两种调用方式:

第25页:函数表达式函数作

函数表达式函数作

为表达式的一项,出现在表达式中,以函数返回值参与表达式的运算。

这种方式要求函数是有返回值的。

例:定义的函数add,允许下列的调用方式:y=add(3.0,4.0);//函数表达式for(y=0,i=1;i<=5;i++)y=add(y,i);//函数表达式if(add(x,y))>0{…}

printf("%d",add(a,b)); 

第26页:4.4 函数的调用和函数说明

4.4 函数的调用和函数说明

不兼容的赋值转换(某些类型之间不能进行赋值,如指针和实型)并不给出错误信息,程序继续运行,但结果不正确,这点应特别注意。

  • 函数名后面的列表内的参数叫实参
  • 调用的函数名字必须与定义的名字完全一致
  • 实参个数与形参个数应一致,类型也应统一
  • 如果不匹配,系统进行自动转换。
  • 函数可以嵌套调用,如:a函数调用b函数,b函数中又调用c函数。(但数不许嵌套定义)
  • 实参表求值顺序,因系统而定(VC、TC自右向左)4.4函数的调用和函数说明

第27页:例3*

例3*

main(){ inti=2,p; p=f(i,++i); printf("p=%d\n",p); } intf(inta,intb){ intc; if(a>b)c=1; elseif(a==b)c=0; elsec=-1; return(c); } 4.4函数的调用和函数说明pi等价p =03f (2, 2); f ( i, i++); 03f (2, 2); f ( i++, i); 03f (3, 3); f ( i,++i); 13f (3, 2); f (++i, i); 14f (4, 3); f (++i, ++i); 14f (3, 2); f (++i, i++); 04f (3, 3); f (i++, ++i); 04f (2, 2); f (i++, i++); 换成运行结果:p = 0后置的++不参加参数计算,但在参数计算完后,i还是加1。
  • 参数求值顺序

第28页:*:写程序,从键盘读入两个浮点数

*:写程序,从键盘读入两个浮点数

z=11.900000lf与double匹配,若不匹配,结果会错!若把l去掉,结果如何?

#include<stdio.h>doubleadd(double,double); doublex,y,z; printf("z=%lf\n",z); } doubleadd(doublea,doubleb){ return(a+b); } 4.4函数的调用和函数说明运行结果:3.
  • 求两个双精度浮点数的和。

第29页:函数说明C语言规定

函数说明C语言规定

函数必须先定义,后调用。

但实际写程序时,由于函数之间可能互相调用,也很难保证这种关系总是成立。

解决这种问题的方法是在调用之前对函数进行说明,这种方法称为函数的原型说明。

#include<*.h>
  • 对被调用函数要求
  • 必须是已存在的函数
  • 用户自定义函数:函数原型说明4.4函数的调用和函数说明

第30页:函数说明一般形式函数类型函数名

函数说明一般形式函数类型函数名

函数说明作用告诉编译系统函数类型、参数个数及类型,以便检验,因此参数列表中,只需类型名即可。

如形参名可以是任意的用户标识符,不一定要和函数定义的形参名相同。

4.4函数的调用和函数说明

  • (形参类型[形参名],…);或函数类型函数名()

第31页:函数说明举例*

函数说明举例*

#include<stdio.h>main(){ floatadd(float,float); scanf("%f%f",&a,&b); c=add(a,b); printf("sumis%.2f\n",c); } floatadd(floatx,floaty){ returnx+y; } 4.4函数的调用和函数说明上机调试,把函数说明这一行去掉,看看结果?(sum is 4.000000)运行结果:3.
  • sum is

第32页:4.4 函数的调用和函数说明

4.4 函数的调用和函数说明

若函数返值是char或int型,系统自动按int型处理。

不鼓励使用这种用法,原因是在这种情况下C语言编译器无法得到关于函数参数和参数类型的信息。

(在VC中该情况无效)4.4函数的调用和函数说明

  • 函数原型说明的形式可理解为函数定义的首都加分号“;”
  • 函数说明位置:数据说明部分(函数内或外)
  • 函数定义与函数说明不同
  • 可以省去对被调用函数的说明两种情况
  • 被调用函数定义出现在主调函数之前。

第33页:例4*

例4*

被调函数出现在主调函数之前,不必说明。floatadd(floatx,floaty){floatz;z=x+y;returnz;}

main(){ floata,b,c; scanf("%f,%f",&a,&b); c=add(a,b); printf("sumis%f\n",c); } 4.4函数的调用和函数说明

第34页:例5*

例5*

main( ){ floata, b; int c; scanf("%f,%f", &a, &b); c = max(a); printf("max is %d\n",c); } max (floatx,floaty){ return( x>y ? x : y ); } 4.4函数的调用和函数说明错误!但没有错误信息的报告!c=max(a,b); 应改为
  • int型函数可不作函数说明(在VC中无效)

第35页:函数原型

函数原型

说明给程序设计带来了极大的灵活性在一个比较复杂的设计中,总是包含很多函数,通过使用函数原型说明,可以随意安排函数定义的位置,如既可以把主函数写在所有其他函数的后面,也可以把主函数写在所有其他函数的前面。

4.4函数的调用和函数说明

第36页:4.5 调用函数和被调函数的数据传递

4.5 调用函数和被调函数的数据传递

  • 形式参数与实际参数
  • 形参定义函数时函数名后面括号中的变量名。只能在该函数体内使用。
  • 实参调用函数时函数名后面括号中的表达式。
  • 作用实现数据传送。二、函数间的数据传递方式
  • 实参和形参之间进行数据传递。
  • return语句把函数值返回调用函数。
  • 通过全局变量进行数据传递(稍后讨论)

第37页:6:调用函数和被调函数

6:调用函数和被调函数

main(){ floatadd(float,float); c=add(a,b);} floatadd(floatx,floaty)//x,y为形参{ return(x+y); } 4.5

第38页:参数传递方式

参数传递方式

值传递方式函数调用时,为形参分配单元,并将实参的值复制到形参中;调用结束,形参单元被释放,实参单元仍保留并维持原值。

单向传递,即只能把实参的值传送给形参,而不能把形参的值反向地传送给实参。4.5调用函数和被调函数的数据传递值传递!

  • 值传递特点
  • 形参与实参占用不同的内存单元
  • 即使同名也互不影响

第39页:实参与形参结合的本质C语言的函数调用机制

实参与形参结合的本质C语言的函数调用机制

是很简单的,值传递是实参与形参结合的唯一方法,后面实参的其他情形,离不开值传递这个本质。

无论实参是何种类型的量,在函数调用时,都必须具有确定的值,以便把这些值传送给形参;

4.5调用函数和被调函数的数据传递floatadd ( x, y)floatx,y;{return(x+y); }

main( ){ floata=5, b=8, s; s=add(a+b,3); printf("s=%f", s); }
  • 说明
  • 实参可以是常量、变量、表达式、函数等

第40页:形参必须指定类型

形参必须指定类型

形参变量只有在被调用时,才分配内存单元;

调用结束时,即刻释放所分配的单元。

因此,形参只有在该函数内有效。

调用返回后,则不能再使用该形参变量。

4.5调用函数和被调函数的数据传递floatadd ( x, y)floatx,y;{return(x+y); }

main( ){ floata=5, b=8, s; s=add(a+b,3); printf("s=%f", s); }
  • 形参与实参类型一致,个数相同
  • 若形参与实参类型不一致,自动按形参类型转换—函数调用转换

第41页:例7*以下程序想通过调用swap

例7*以下程序想通过调用swap

main(){ intx=10,y=20; printf("1.x=%d,y=%d\n",x,y); swap(x,y); printf("2.x=%d,y=%d\n",x,y); } swap(inta,intb){ inttemp; temp=a; a=b; b=temp; } 运行结果:1. x=10, y=202. x=10, y=204.5
  • 交换主函数中变量x和y中的数据。请观察运行结果。
  • 调用前:1020x:y:
  • 调用:1020x:y:1020a:b:
  • swap:temp:1020x:y:2010a:b:
  • 调用结束:1020x:y:

第42页:例8*设计程序

例8*设计程序

#include<math.h>isprime(inta)//若a是素数,返回1; 否则返回0/{ inti; for(i=2; i<=sqrt(a); i++)if(a%i==0)return0; return1; } main(){ intx; printf("输入一个整数:"); scanf("%d",&x); if(isprime(x))printf("%d是素数!\n",x); elseprintf("%d不是素数!\n",x); } 4.5调用函数和被调函数的数据传递运行结果:输入一个整数: 17
  • 判断一个整数是否为素数。
  • 17是素数!

第43页:嵌套调用

嵌套调用

main( )调用函数a后续语句结束a函数调用函数b后续语句returnb函数return4.6
  • 是指,在执行被调用函数时,被调用函数又调用了其它函数(但定义不可嵌套)。关系图如下:

第44页:9*求三个数中最大数和最小数

9*求三个数中最大数和最小数

main(){ inta,b,c,d; scanf("%d%d%d",&a,&b,&c); d=dif(a,b,c); printf("Max-Min=%d\n",d); } intdif(intx,inty,intz){ returnmax(x,y,z)-min(x,y,z); } 4.6函数的嵌套调用与递归函数intmax(x, y,z)intx, y, z; { intr; r=x>y?x:y; return(r>z?r:z); } intmin(x, y, z)intx, y, z; { intr; r = x<y?x:y; return( r<z?r:z); }
  • 的差值intdif(intx,inty,intz);intmax(intx,inty,intz);intmin(intx,inty,intz)

第45页:f( )调

f( )调

用f( )直接调用二、递归调用函数的递归调用是指,一个函数在它的函数体内,直接或间接地调用它自身。

4.6函数的嵌套调用与递归函数intf(intx){inty, z;…z =f(y);…return(2*z);}f1()f2( )调用f2( )调用f1( )间接调用intf1(intx){inty, z;…z=f2(y);…return(2*z);}intf2(intt){inta, c;…c =f1(a);…return(3+c);}

第46页:4.6 函数的嵌套调用与递归函数

4.6 函数的嵌套调用与递归函数

在递归调用中,调用函数又是被调用函数,执行递归函数将反复调用其自身。

每调用一次就进入新的一层并在内存堆栈区分配空间,用于存放函数变量、返回值等信息,所以递归次数过多,可能引起堆栈溢出;

系统对递归函数的自调用次数没有限制。

为了防止递归调用无终止地进行,必须在函数内有终止递归调用的手段。

常用的办法是加条件判断,满足条件后就不再作递归调用;

4.6函数的嵌套调用与递归函数

第47页:C提供递归函数

C提供递归函数

有其深远的数学和现实背景;

两个数学中的递归定义实例:定义的右边使用了定义本身。

如左式描述的计算方法中,把一个数的阶乘用比它小1的数的阶乘来定义,最终归结到0的阶乘。

如果程序设计语言支持能用同样的方法定义计算过程,类似问题的求解将非常简单。

C语言确实提供了递归地调用函数的机制。

4.6函数的嵌套调用与递归函数

第48页:所有的递归问题都可用非递归的方法解决

所有的递归问题都可用非递归的方法解决

递归的关键是抽象出递归模型,要把递归问题转化为一个新问题,且这个新问题的解法仍与原来的解法相同,只是处理规模有规律地递减。

一个问题是否使用递归方法解决,需要在效率和简洁易懂的程序代码之间取得平衡。4.6函数的嵌套调用与递归函数

  • 使用递归算法写出的程序在算法的可读性和正确性方面都比同一问题的非递归程序更好。
  • 采用递归程序的问题是尽管代码比非递归程序紧凑,但其程序执行的效率不如非递归程序。
  • 另一方面,一些比较复杂的递归问题用非递归实现,往往使程序变得十分复杂而且难以理解。

第49页:例10*n!的递归函数longfact

例10*n!的递归函数longfact

printf("Err!"); elseif(n==0)f=1; elsef=n*fact(n-1); returnf; } main(){ longn,y; scanf("%d",&n); y=fact(n); printf("%d!=%ld",n,y); } 4.6函数的嵌套调用与递归函数调用过程(设n为4)fact(4)=4*fact(3)fact(3)=3*fact(2)fact(2)=2*fact(1)fact(1)=1*fact(0)fact(0)=11126=24
  • (intn)//形参只一个{longf;if(n<0)

第50页:例11设计打印汉诺塔问题金盘移动步骤

例11设计打印汉诺塔问题金盘移动步骤

的程序。

Hanoi塔问题,是经典的递归问题,研究过递归的人没有几个不知道它,传说它是古印度僧侣们玩的一种游戏,游戏的装置是一块铜板上面有三根杆A、B、C,最左边杆自上而下、由小到大串上64个金盘构成一个塔,游戏的目标是把左边杆A上的金盘全部移到最右边的杆C上,条件是一次只能移动一个盘,并且不允许大盘在小盘上面。

4.6函数的嵌套调用与递归函数ABC

第51页:4.6 函数的嵌套调用与递归函数

4.6 函数的嵌套调用与递归函数

  • 要把64个盘从A杆移到C杆,临时放盘杆为B杆。很难直接写出每一个具体步骤。
  • 能否把问题变得简单些?若有办法把63个盘子从一个杆移到另一个杆,就有解决方法:
  • 将63个盘子从A杆移到B杆。
  • 把最下面的盘子从A杆移到C杆。
  • 将63个盘子从B杆移到C杆。4.6函数的嵌套调用与递归函数ABC

第52页:问题转化移动64个盘子的问题

问题转化移动64个盘子的问题

转化成移动63个盘子的问题;转化成移动62个盘子的问题;…转化成移动1个盘子的具体操作。

  • 以上过程就是递归过程
  • 递归的结束条件是只剩下一个盘子。4.6函数的嵌套调用与递归函数

第53页:程序*voidmove

程序*voidmove

printf("%c--> %c\n", getone, putone); } voidhanoi(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(){ intm; printf("输入碟数:"); scanf("%d",&m); printf("移动%3d碟步骤:\n",m); hanoi(m,'A','B','C'); } 4.6函数的嵌套调用与递归函数运行结果:输入碟数:3
  • (chargetone,charputone){
  • 移动3碟步骤:A-->CA-->BC-->BA-->CB-->AB-->CA-->C

第54页:4.7 变量的作用域和生命期

4.7 变量的作用域和生命期

  • 大的源程序在处理中存在的问题
  • 编辑时,由于屏幕只显示文件的很小一部分翻阅和查找费时,处理起来很麻烦
  • 调试时,个别地方的修改引起整个文件重新编译
  • 开发过程中,程序先完成的部分与正开发的部分混在一个源程序中,调试新的部分时有可能无意中修改了已调试好的部分
  • 更进一步,一个大的程序有可能是许多人共同合作,无法让他们使用同一个源程序。

第55页:合理的程序结构

合理的程序结构

合理的结构应该是将程序组织在若干个长度合适的源程序文件内,对每个源程序单独编译、调试,再将所有编译好的目标模块连接起来。

这些源程序文件本质上是处理同一问题的不同片段,它们之间或多或少会发生某些关系,比如会有公用的变量、互相调用的函数等。

  • 解决大的源程序存在的问题,必须把程序结构组织好。
  • 本节讨论的问题是理解和实现多文件结构程序的基础。4.7变量的作用域和生命期

第56页:变量使用中的几个问题

变量使用中的几个问题

当一个程序的若干函数分别存放于不同的源程序文件中时有什么限制?本节将讨论C语言对这些问题的规定。4.7变量的作用域和生命期

  • 在C语言里,变量必须先定义后使用,但定义语句应该放在什么地方?
  • 在程序中,一个定义了的变量是否随处可用?(这些问题都涉及变量的作用域)
  • 经过赋值的变量是否在程序运行期间总能保存其值?(这就涉及到变量的生存期)

第57页:“定义”和“说明”的严格区别

“定义”和“说明”的严格区别

一个C源程序文件由一系列的外部定义和外部说明构成。所谓外部,是指写在源程序文件表层相对于出现在函数体里面的定义或说明而言的。

函数定义是外部定义。在外层(不在任何函数体内)。一个外部定义或说明总是从它在源程序文件中出现位置开始起作用,直到该源文件结束。

C语言规定函数只能在函数外部定义,不许在函数的内部定义另一个函数,这样,所有函数都定义在程序的表层,整个程序的结构比较简单。4.7变量的作用域和生命期

  • “定义”是指给变量分配确定的存储单元,“说明”只是说明变量的性质,并不分配存储空间。

第58页:变量定义的有关知识

变量定义的有关知识

程序运行过程中,该变量的存储空间什么时候分配和撤销。(存在期)后两点是这节要讨论的作用域和存在期。4.7变量的作用域和生命期

  • 定义一个变量的意义
  • 给变量定义一个名字,在程序中通过名字可以操作该变量的存储空间,进行取值和赋值
  • 定义了该变量存储空间中数据的存在形式、范围和可以进行的运算
  • 程序中哪些地方可以使用该变量;(作用域)

第59页:变量的作用域

变量的作用域

是源程序中的某一部分,在这个范围里,变量定义是有效的,可以使用该变量的名字进行与该变量有关的操作,每个变量都有一个确定的“作用域”,由变量定义出现的位置确定。

作用域是变量的作用范围,一个定义的作用域是源程序中的一段,可以从源程序正文中把有关的程序段划分出来,因此作用域是静态概念,与程序的执行过程无关。

4.7变量的作用域和生命期

第60页:变量的存在期

变量的存在期

C语言对变量的操作,实际上是对分配给变量的存储单元的操作。

事实上并不是所有的变量都是在程序一开始就建立好,直至程序结束才撤销,程序里各种变量存在的时间也可能有所不同。

C语言把变量在程序中存在的那段时期称为该变量的“存在期”。

存在期是动态概念,讲的是程序执行的一段时期,在一个变量的存在期里,它所占的存储单元一直保留,只要不对变量重新赋值,单元中的值就保持不变。

4.7变量的作用域和生命期

第61页:存储空间的组成供用户程序使用的内存空间

存储空间的组成供用户程序使用的内存空间

分为三部分:4.7变量的作用域和生命期具体数据存放的内容区域代码、常量。

程序代码程序区常量、静态局部变量、全局部变量。

在程序执行过程中占据固定的存储单元,在程序开始时分配,结束时释放的数据。

静态存储区函数形参、自动变量,调用函数时现场保护数据和返回地址。

存放程序运行中动态地进行分配和释放的数据。

动态存储区

第62页:存储类别

存储类别

四种与存储类别有关的说明符auto(自动)static(静态)register(寄存器)extern(外部)

例子autointi,j;staticintn;externintm;registerk;4.7变量的作用域和生命期

  • 指的是变量在内存中的存储位置,有静态和动态两种存储类别。
  • 变量定义格式[存储类别]数据类型变量表
  • 作用根据变量的存储类别可以知道变量的作用域和存在期。

第63页:4.7 变量的作用域和生命期应该建立对变

4.7 变量的作用域和生命期应该建立对变

  • 量说明的总体概念。C的变量说明应该包括两方面的信息:
  • 存储类别
  • 数据类型

第64页:局部变量的作用域和生命期

局部变量的作用域和生命期

局部变量是在一个函数内部或复合语句内部定义的变量(复合语句中也可定义局部变量例14)。

作用域只在函数或复合语句体范围内有效,在此函数或复合体外不能使用这些变量;

局部变量可使用auto、static和register说明符。

4.7变量的作用域和生命期

第65页:auto(自动)变量局部变量定义时

auto(自动)变量局部变量定义时

存储空间分配系统对自动变量是动态分配存储空间的,数据存储在动态存储区中,像函数中的形参和在函数中定义的变量,在调用函数时系统会给它们分配存储空间,在函数调用结束时就自动释放这些存储空间,因此,这类局部变量称为自动变量。

4.7变量的作用域和生命期用户区程序区静态存储区动态存储区

  • 使用auto说明符或没指定存储类别,系统就认为所定义的变量为自动类别。

第66页:auto常省略自动变量是使用最多

auto常省略自动变量是使用最多

的局部变量,auto常省略。前面使用的变量并没有声明为auto,都隐含指定为自动变量。如:int k;和auto int k;是等价的。

  • 定义位置放在函数体或复合体中所有可执行语句前。
  • 自动变量作用域例12
  • 从定义的位置起,到函数体或复合体结束止。
  • main中定义的变量只在main中有效
  • 形参属于局部变量,在函数内有效。4.7变量的作用域和生命期

第67页:例12

例12

局部变量的作用域floatf1(inta){intb,c;……}charf2(intx,inty){inti,j;……}

main(){ intm,n; …… } 4.7变量的作用域和生命期a,b,c:仅限于函数f1()中有效x,y,i,j:仅限于函数f2()中有效m,n:仅限于main()中有效,main

第68页:自动变量存在期它的存储单元

自动变量存在期它的存储单元

在程序运行进入这些局部变量所在的函数体(或复合体)时生成,退出其所在函数体(或复合体)时消失;

当再次进入函数体(或复合体)时,系统将为它们另行分配存储单元,因此,变量的值不可能被保留。

使用自动变量的优点例14变量局部存于函数中,可造成函数间的信息隔离;

不同函数中使用了同名变量也不会相互影响。

(同名变量占不同内存单元;

例13)可避免因不慎赋值导致的错误影响到其他函数。

4.7变量的作用域和生命期

第69页:例13*

例13*

main(){ inta=3,b=4; sub(); printf("main:a=%d,b=%d\n",a,b); } sub(){ inta,b; b=7; printf("sub:a=%d,b=%d\n",a,b); } 4.7变量的作用域和生命期运行结果:main: a=3, b=4sub: a=6, b=7main: a=3, b=4
  • 不同函数中同名变量

第70页:例14*给出下列程序

例14*给出下列程序

的运行结果intfun(intx)//x是自动变量{if(x>0){intk;//k是自动变量k=2*x;returnk;}elsereturnx;//把x换成k?

}4.7变量的作用域和生命期a的作用范围k的作用范围

main(){ inta=5; printf("a=%d\n",a); } x的作用范围运行结果:a =10

第71页:static(静态)变量

static(静态)变量

空间的分配在静态存储区占据永久性的存储单元,函数退出后下次再进入该函数,静态局部变量仍使用原来的存储单元,所以它的值在函数调用后不消失而保留下来,下次调用时,该变量的值是上次调用结束时的值。

存在期与自动变量有本质区别,一直到程序运行结束。4.7变量的作用域和生命期用户区程序区静态存储区动态存储区

  • 在函数体(或复合体)内部用static说明的变量。
  • 作用域自动变量一样

第72页:例16*局部静态变量值具有可继承性

例16*局部静态变量值具有可继承性

main(){ voidincrement(void); increment(); increment(); increment(); } voidincrement(void){ intx=0; x++; printf("%4d", x); }
main(){ voidincrement(void); increment(); increment(); increment(); } voidincrement(void){ staticintx=0; x++; printf("%4d", x); } 运行结果:1 1 1运行结果:1 2 3
  • 4.7变量的作用域和生命期
  • 一般变量的使用(对比)
  • 局部静态变量的使用

第73页:例17定义一个函数

例17定义一个函数

程序voidcount10(void){staticintm=0;if(++m==10){

结论可见,静态局部变量可以保证局部数据的安全性,实现信息隐藏和保护。具有局部作用域和全程存在期。4.7变量的作用域和生命期

printf("Count10\n"); m=0 } }
  • 它每被调用10次,就输出一个信息到屏幕上。
  • 分析需一计数变量m,每次调用函数,m的值加1,当m到10时,输出信息并将m清0。

第74页:register

register

(寄存器)变量转下一部分寄存器变量也是自动变量,它与自动变量的区别仅在于空间的分配不同。

空间的分配建议编译程序将变量的值保留在CPU的寄存器中,而不像一般变量那样,用内存单元。

作用程序运行时,访问寄存器的速度要比访问内存快得多,将频繁使用的少数变量指定为register变量,有助于提高程序的运行速度。4.7变量的作用域和生命期

第75页:4.7 变量的作用域和生命期

4.7 变量的作用域和生命期

CPU中的寄存器数目有限,在一个函数中允许使用寄存器变量的数目与CPU的类型和使用的C编译程序有关,当没有足够的寄存器来存放指定的变量,或编译程序认为指定的变量不适合放在寄存器中时,指定变量自动按自动变量处理。

寄存器变量的说明应尽量靠近其使用的地方,用完后尽快释放,以提高寄存器的利用效率。如,常把寄存器变量的说明放在复合语句中来实现。4.7变量的作用域和生命期

  • 编写程序时使用寄存器变量只是对编写程序的一种建议,而不是强制性的。
  • 寄存器变量不是内存中,不能进行求地址的运算。

第76页:例15用寄存器变量设计实现xn的函数

例15用寄存器变量设计实现xn的函数

分析本题需要一个进行累乘操作的循环,循环的次数受指数n的控制,循环变量的初始值是指数函数的底x,设x、n和函数的返回值类型均为int,函数名为power。

程序intpower(intx,registerintn){registerintp;for(p=x;n;n--)p=p*x;returnp;}

说明实际实现时,xn的值增加很快,可考虑使用长整型数表示x和函数的返回值。4.7变量的作用域和生命期

第77页:全局变量的作用域和生命期

全局变量的作用域和生命期

存储类别全局变量只有静态一种存储类别,它的存在期是整个程序的运行期间。

使用extern和static两种说明符可以对其作用域加以控制。

4.7变量的作用域和生命期

  • 全局变量在函数外定义的变量称全局变量,也叫外部变量,可为本文件所有函数共用。
  • 作用域从定义变量的位置开始到本源文件结束,及有extern说明的其它源文件。

第78页:4.7 变量的作用域和生命期

4.7 变量的作用域和生命期

在全局变量的作用域中,不应该有重名的全局变量。由于连接时要使用C语言的程序库,也不要定义与程序库中同名的程序对象。

外部变量加强了函数间的联系,但又使得这些函数的独立性降低。从模块化设计观点来看这是不利的,因此应尽量少用外部变量。

在同一源文件中,允许外部变量和内部变量同名。在内部变量的作用域内,外部变量将被屏蔽而不起作用。可把它们看成不同的变量。4.7变量的作用域和生命期

  • 由于全局变量从其定义处开始起作用,常把它们写在程序的最前面,可在任何函数中访问它。

第79页:例18分析运行结果

例18分析运行结果

#include<stdio.h>intt1;voidf2(void); voidf3(void); main(){ t1=5; printf("m1:%d\n",t1); f1(); printf("m2:%d\n",t1); f2(); f3(); } voidf1(){ t1+=5; printf("f1:t1=%d\n",t1); } intt2; intt1; t2=5; printf("f2:t1=%d\n",t1); printf("f2:t2=%d\n",t2); } voidf3(){ t2=15; printf("f3:t2=%d\n",t2); } 4.7变量的作用域和生命期t2没有定义,此句无法实现运行结果:m1:5f1:t1=10m2:10f2:t1=20f2:t2=5f3:t2=15

第80页:使用extern说明符的全局变量

使用extern说明符的全局变量

作用:通知编译程序该变量是一个已在外部定义了的全局变量,已分配了存储单元,不需再为它开辟存储单元。

这时,该全局变量的作用域从extern说明处开始,延伸到该函数结束。

4.7变量的作用域和生命期

  • 提供对该变量先使用后定义的支持
  • 使用情况:同一程序文件中,当全局变量定义在后,引用它的函数在前时,应该在引用它的函数中用对此全局变量进行说明
  • 一般形式:extern数据类型变量表

第81页:4.7 变量的作用域和生命期p,q

4.7 变量的作用域和生命期p,q

的作用范围扩展后c1,c2的作用范围intp=1,q= 5;//外部变量的定义extern charc1,c2;//外部变量的说明float f1(int a){ int b, c;……; }int f3(){……}charc1,c2;//外部变量的定义char f2( int x, int y){ int i, j ;……; }

main(){ int m,n; …… } c1,c2的作用范围

第82页::外部变量的定义和说明不同

:外部变量的定义和说明不同

在不同的文件间扩展全局变量的作用域在多个文件中需引用同一个全局变量时,若在每个文件中都定义一个同名的全局变量,单独编译时并无异常,但在连接时会产生“重复定义”错误。

解决的方法是在一个文件中定义全局变量,在其他要引用的文件中用extern对变量进行说明。

常常在一个文件中定义所有的全局变量。

4.7变量的作用域和生命期内存位置次数不同分配,可初始化函数之外只1次外部变量定义不分配和初始化函数内或外可多次外部变量说明

第83页:例19有两个文件file

例19有两个文件file

c和file2.c,分析运行结果。4.7变量的作用域和生命期file1.c程序文件:intx;//定义全局变量externintz;//说明变量

main(){ fun1(); printf("z = %d\n", z); fun2(); fun3(); } intz=5; x=10; z+=15; } file2.c程序文件:externintx; printf("x=%d\n",x); } fun3(){ x++; printf("x=%d\n",x); } 运行结果:z = 20x = 10x = 11

第84页:使用static说明符的全局变量

使用static说明符的全局变量

静态全局变量定义全局变量时,用static说明符说明的全局变量,称为静态全局变量。

静态全局变量只限于本程序文件内使用,不能被其他程序文件所引用。

(限定作用域)

作用使用static说明符,可以使多个开发程序的人,在自己的程序文件中使用同名的全局变量名而互不干扰,为程序的模块化、通用化提供方便。

4.7变量的作用域和生命期

第85页:例20分析程序执行结果两个程序文件fil

例20分析程序执行结果两个程序文件fil

e1.c和file2.c的内容如下:4.7变量的作用域和生命期程序文件file1 .c:extern int m;//说明全局变量

main( ){ intn=5; printf ("file1: %d\n",n); printf ("file2: %d\n",m); } 程序文件file2.c:static int m=20; ……//定义静态全局变量m无关分别编译两个文件正常,但连接时指示“m无定义”的错误,由于在file2中m使用static说明符,其他文件中的m与file2中的m无关。

第86页:可以发现

可以发现

四个说明符,只有extern说明与变量定义分开使用,auto、static和register说明符都在定义变量时进行说明。

  • 从作用域角度来看,变量分为全局变量和局部变量。
  • 从存在期角度来看,变量分为静态变量和动态变量。

第87页:变量初始化

变量初始化

初始化的方法直接用字面量或用字面量、符号常量及基本运算符号构造表达式,不能包括各种涉及赋值的运算(如++、--等)。

  • 存储在静态存储区的变量(外部变量、静态变量)
  • 初始化时刻在程序执行前完成,只进行一次。
  • 初始化表达式的限制只能使用不需要执行程序(在编译阶段)就能求出一个常量值的表达式。
  • 默认值若不初始化,则系统自动初始化为0。4.7变量的作用域和生命期

第88页:存储在动态存储区的变量

存储在动态存储区的变量

初始化的方法每次调用时重新分配存储单元,调用后存储单元释放,因此,每次建立时均需重新初始化。

默认值若在定义时不进行初始化,则变量的值是不确定的。(此时,程序在使用变量前,一定要为变量赋值,VC会提出警告。)4.7变量的作用域和生命期

  • (局部变量)
  • 初始化时刻程序执行过程中在调用对应函数时动态分配的。
  • 表达式的限制没有限制,符合类型约束即可。

第89页:21分析下列初始化操作

21分析下列初始化操作

的结果inta,b=5;//定义全局变量a,b//b初始化为5,a为默认值0floatf=5.0;//定义全局变量f,初始化为5.0fun(intx,inty)//函数形参是局部变量//由调用实参进行初始化{inti,j=0;//定义局部变量i,j…//j初始化为0,i初值不确定}4.7变量的作用域和生命期

第90页:外部函数和内部函数

外部函数和内部函数

外部函数定义函数时,在返回值类型前加extern,此函数为外部函数。extern可以省略,以前我们定义的函数都是外部函数。

调用方法通常,当函数调用语句和函数不在同一文件内时,应在调用前使用带extern的原型说明。

如:externfloatfunc (float,int);说明其他程序文件中定义了函数func。

4.7变量的作用域和生命期

  • 特征可以被其他程序文件所调用。

第91页:内部函数定义函数时

内部函数定义函数时

C语言不允许在函数内部定义另一个函数,所有函数在本质上都是外部函数,但定义函数时可以使用exetrn和static说明符。

#include<…>将相关的说明包含进程序文件。4.7变量的作用域和生命期
  • 在返回值类型前加static此函数为内部函数,又叫静态函数。
  • 特征只限于本文件范围内的其他函数调用。
  • 作用使用内部函数,可以避免不同程序文件中因函数名相同而引起混乱。
  • 说明
  • C的库函数均为外部函数,用

第92页:4.8 多文件程序的组织和调

4.8 多文件程序的组织和调

试方法转一、概述多个程序文件组织大型程序,目的是能够独立地对单个源程序文件进行调试。

由于这些源程序文件通常不是一个完整的C程序,所以不能独立地连接和执行。

为了进行程序的分块开发,需要一些辅助的调试手段。

在进行实际项目开发时,程序开发者通常临时写一些用于调试的主程序,和这些程序文件进行连接,以发现其中的问题。

在本课程的学习中,由于问题较简单,可采用对各个源程序文件先进行单独编译,排除其中的语法错误,再进行最后的连接和运行的方法。

第93页:TC集成环境

TC集成环境

中,提供了对多文件程序开发的支持,这些工作通过TC的项目文件来组织。

一个项目就是一个待开发的程序,它由多个源程序文件和若干头文件组成。

观察C语言系统本身的组织,系统为用户提供一组标准和附加(服务于特定系统)的头文件,同时提供一些函数库。

头文件的作用是为在C程序中使用库函数提供必要的辅助信息。

如果要在程序中使用库函数,只要在源文件前面包含了必要的头文件,就能保证对有关函数的正确调用。

我们进行程序开发也应该遵守这样的规律:将程序的源文件分成两类,一类是包含实际代码的程序文件,用.c做后缀,另一类是为程序文件提供必要信息的辅助文件用.h做后缀。

4.7多文件程序的组织和调试方法

第94页:多文件程序的组织方法

多文件程序的组织方法

按功能把程序划分为几个文件,分别定义程序的所有外部变量和函数,每个文件中定义的变量和函数或属于一类,或联系较密切,形成一个整体。

主函数单独建立一个文件,也可包含少数关系密切的函数。

程序中公共的外部变量一般在主函数中定义。

其他的外部变量在使用它的文件中定义,所有局部于某个程序文件的东西都应该写在这个文件中,局部变量和函数应使用static定义。

4.7多文件程序的组织和调试方法

  • 将多文件的程序组织起来的方法

第95页:建立一个或若干个头文件

建立一个或若干个头文件

对所有在一个文件里定义而要在其他文件中使用的东西,都需要在一个头文件中有它的说明。

把公用的宏、类型定义放在适当的头文件中,作为各个文件的参考。

头文件中只写那些不实际生成代码,不实际导致存储分配的东西。

可以包括:必要的标准库和其他包含命令,多个文件中使用的公共宏定义,各种公共的类型定义和说明,函数的原型说明,变量的外部说明。

4.7多文件程序的组织和调试方法

第96页:若某程序文件需要使用该文件中没有定义

若某程序文件需要使用该文件中没有定义

一个程序文件中可能有几个包含命令,有的头文件里还包含其他头文件,这样有可能引起同一个头文件被重复包含的问题,有时可导致编译过程不能正常进行。

可通过例15的方法解决重复包含的问题。

4.7多文件程序的组织和调试方法

#include包含相应的头文件。包含命令在程序文件的开头使用,使整个文件可以看到需要的说明。这种方法可以保证所有使用的地方看到同一个说明。程序文件中,系统头文件的包含使用<>括起头文件名的形式,对自定义头文件的包含使用""括起头文件名的形式。

第97页:例解决头文件重复包含问题

例解决头文件重复包含问题

解决头文件重复包含的问题需要利用第5章介绍的预处理命令,使第二次包含时,能在已替换的源代码中找到控制该次包含是否进行的标志。

C语言系统的头文件一般都采用了这种方法。本例中,宏的概念在第5章才会介绍,这里请注意的是解决重复包含问题的程序语句结构问题。

在自定义的头文件中加入下列结构:#if!defined(MY_HEAD_FILE)#defineMY_HEAD_FILE…//原来的头文件内容#endif4.7多文件程序的组织和调试方法指定的一个宏名字只要套用这个结构,将原来头文件的内容插在此处。

第98页:defined是C语言系统提供的特殊

defined是C语言系统提供的特殊

的谓词,当MY_HEAD_FILE是有定义的宏名字时,得到1的结果,否则为0。

采用这种形式后,如果这个文件第二次被包含时,由于MY_HEAD_FILE已经有了定义,#if和#endif之间的所有东西都将被丢掉。

现在不会产生重复包含的问题了。

查看C系统的头文件时,可能已发现头文件中的许多宏名字用下划线“_”开头,目的是区别于普通变量和程序中使用的宏定义的宏名字。4.7多文件程序的组织和调试方法

  • 请读者在第5章的内容学习完毕之后,再回到本例仔细体会本例所示例的结构。

第99页:多文件程序的运行调试方法

多文件程序的运行调试方法

例程序包括两个文件,file1.c和file2.c,并将它们连接成一个执行程序,写出运行结果。

4.7多文件程序的组织和调试方法file1文件如下:intx=0, y=10;//定义全局变量voidadd(void){ y =10 + x;//使用全局变量x*= 2;}

main (){ extern voidsub(); x+=5; add(); sub(); printf ("x=%d, y=%d", x, y); } file2.c文件如下:voidsub(void){ externintx; x-=5; } 说明函数sub()的定义不在本程序文件内说明变量x的定义不在本文件内运行结果:x=25, y=25

第100页:连接方法

连接方法

将其保存为文件work.prj,扩展名必须为.prj。

如果file1.c和file2.c不在当前目录下,应写出路径。

项目文件内容还可以是目标文件和函数库。

4.7多文件程序的组织和调试方法

  • 编辑源文件输入并编辑两个源文件,以file1.c和file2.c将它们保存。
  • 建立项目文件假设这个程序叫work,新建一个文件,输入文件内容:file1.c
  • file2.c

第101页:打开项目文件在TuboC主菜单中选Pro

打开项目文件在TuboC主菜单中选Pro

ject菜单中的Projectname选项,屏幕上出现一个对话框,输入项目文件名字work.prj,表示当前要编译的是work.prj中包括的文件。

(用完清除该选项)

编译连接按F9,进行连接,系统将两个文件翻译成目标文件,并把它们连接成work.exe。

如果编译过程中出现错误,系统将停止在出错的文件上,并将文件调入编辑器。

根据信息窗口的提示信息,可修改源程序,重复编译连接过程。

执行按Ctrl+F9,运行可执行文件work.exe。4.7多文件程序的组织和调试方法

第102页:提示使用多个程序文件分块设计是组织大

提示使用多个程序文件分块设计是组织大

中型程序设计的基本方法,进行这方面的练习,一是体会C语言变量作用域和存在期对程序的影响,二是学会多人协同合作共同完成软件设计任务的基本要领。

4.7多文件程序的组织和调试方法

第103页:4.9 小结函数是组成C语言程序的基本单

4.9 小结函数是组成C语言程序的基本单

变量的存储类型。

包括变量存在期与作用域的概念,4种变量存储类别的说明方式。

特点和使用范围,不同存储类型变量在使用时的区别,变量的初始化方法,在函数间使用外部变量传递数据的规定。

  • 位,是程序模块化、结构化设计的基础,本章关于函数和程序结构的讨论主要包括以下内容。
  • C语言关于函数的规定。包括定义方法、说明规定、返回值、函数返回和函数调用。
  • 函数之间参数传递的规定。包括形参与实参的对应关系,参数传递方式,void型函数。
  • 多文件程序的组织和实现方法。
  • 常见库函数的使用方法。