(Word修改版)C语言程序设计课件

上传人:101****457 文档编号:53705278 上传时间:2018-09-04 格式:DOCX 页数:891 大小:4.34MB
返回 下载 相关 举报
(Word修改版)C语言程序设计课件_第1页
第1页 / 共891页
(Word修改版)C语言程序设计课件_第2页
第2页 / 共891页
(Word修改版)C语言程序设计课件_第3页
第3页 / 共891页
(Word修改版)C语言程序设计课件_第4页
第4页 / 共891页
(Word修改版)C语言程序设计课件_第5页
第5页 / 共891页
点击查看更多>>
资源描述

《(Word修改版)C语言程序设计课件》由会员分享,可在线阅读,更多相关《(Word修改版)C语言程序设计课件(891页珍藏版)》请在金锄头文库上搜索。

1、第一章本章要点C语言的特点C程序的结构在计算机上运行C程序的方法1-1 语言出现的历史背景C语言是国际上广泛流行的高级语言。C语言是在B语言的基础上发展起来的。B (BCPL)语言是1970年由美国贝尔实验 室设计的, 并用于编写了第一个UNIX操作 系统,在PDP 7上实现。优点:精练,接近硬 件,缺点:过于简单,数据无类型。1973年贝尔实验室的D.M.Ritchie 在B语言 的基础上设计出了C语言,对B取长补短, 并用之改写了原来用汇编编写的UNIX,(即 UNIX第5版),但仅在贝尔实验室使用。1-1语言出现的历史背景1975年UNIX第6版发布,C优点突出引起关注。 1977年出现

2、了可移植C语言编译程序 , 推动了UNIX在各种机器上实现 ,C语言也得 到推广,其发展相辅相成。 1978年影响深远的名著The C Programming Language由 Brian W.Kernighan和Dennis M.Ritchie 合著,被称为标准C。 之后,C语言先后移植到大、中、小、微型计 算机上,已独立于UNIX和PDP,风靡世界,成为 最广泛的几种计算机语言之一。1-1语言出现的历史背景 1983年,美国国家标准化协会(ANSI)根据C语 言各种版本对C的发展和扩充,制定了新的标 准ANSI C ,比标准C有了很大的发展。1988年K & R按照 ANSI C修改了他

3、们的The C Programming Language。1987年,ANSI公布了新标准87 ANSI C。1990年,国际标准化组织接受了87 ANSI C为 ISO C 的标准(ISO98991990)。1994年,ISO又修订了C语言标准。目前流行的C语言编译系统大多是以ANSI C为 基础进行开发的。1-1语言出现的历史背景说明:说明:说明:说明:不同版本的C编译系统所实现的语言 功能和语法规则又略有差别,因此读者 应了解所用的C语言编译系统的特点(可 以参阅有关手册)。本书的叙述基本上以 ANSI C 为基础。说明:不同版本的C编译系统所实现的语言 功能和语法规则又略有差别,因此读

4、者 应了解所用的C语言编译系统的特点(可 以参阅有关手册)。本书的叙述基本上以 ANSI C 为基础。1-2 语言的特点(1)语言简洁、紧凑,使用方便、灵活。 32 个关键字、9种控制语句,程序形式自由(2)运算符丰富。34种运算符(3)数据类型丰富,具有现代语言的各种数据 结构。(4)具有结构化的控制语句 ,是完全模块化 和结构化的语言。(5)语法限制不太严格,程序设计自由度大。1-2 语言的特点(6)允许直接访问物理地址,能进行位操 作,能实现汇编语言的大部分功能,可直 接对硬件进行操作。兼有高级和低级语 言的特点 。(7)目标代码质量高,程序执行效率高。 只比汇编程序生成的目标代码效率低

5、10-20%。(8)程序可移植性好(与汇编语言比)。 基本上不做修改就能用于各种型号的计 算机和各种操作系统。1-2 语言的特点问题:既然有了面向对象的C+语言,为什 么还要学习C语言?解释解释解释解释1111:C+是由于开发大型应用软件的需 要而产生的,并不是所有的人都要去编 写大型软件;解释解释解释解释222:面向对象的基础是面向过程。C+ 是面向对象的语言,C是面向过程的,学 起来比C语言困难得多,所以不太适合程序设计的初学者。解释1:C+是由于开发大型应用软件的需 要而产生的,并不是所有的人都要去编 写大型软件;解释2:面向对象的基础是面向过程。C+ 是面向对象的语言,C是面向过程的,

6、学 起来比C语言困难得多,所以不太适合程 序设计的初学者。语言程序介绍语言程序介绍语言程序介绍语言程序介绍语言程序介绍语言程序介绍语言程序介绍语言程序介绍说明: 本程序的作用是输出一行信息:1-3 简单的CThisisaCprogram.#include voidmain( )printf (This is a C program.n);说明:说明:说明:说明: main-主函数名, void-函数类型每个C程序必须有一个主函数main 是函数开始和结束的标志,不可省每个C语句以分号结束使用标准库函数时应在程序开头一行写:#include 说明: main-主函数名, void-函数类型每个C

7、程序必须有一个主函数main 是函数开始和结束的标志,不可省每个C语句以分号结束使用标准库函数时应在程序开头一行写:#include /*文件包含*/*主函数 */*函数体开始*/*输出语句*/*函数体结束*/说明: 输出一行信息:sumis579例1.2 求两数之和#include void main( )/*求两数之和*/int a,b,sum;/*声明,定义变量为整型*/*以下3行为C语句 */ a=123; b=456;sum=a+b;printf(sum is %dn,sum);说明:说明:说明:说明:/*/表示注释。注释只是给人 看的,对编译和运行不起作用。所以可以用 汉字或英文字

8、符表示,可以出现在一行中 的最右侧,也可以单独成为一行。说明:/*/表示注释。注释只是给人看的,对编译和运行不起作用。所以可以用汉字或英文字符表示,可以出现在一行中 的最右侧,也可以单独成为一行。例1.3 求3个数中较大#include void main( )/* 主程序运行情况如下:者。函数*/8,5(输入8和5赋给a和b)max=8(输出c的值)得到的值赋给c */*输出c的值*/int max(int x,int y); / 对被调用函数max的声明 */ int a, b, c;/*定义变量a、b、c */ scanf(d,d,&a,&b); /*输入变量a和b的值*/c=mmaax

9、x(aa,bb);/*调用max函数,将intmax(int x,int y);printf(max=dn,c);说明:说明:说明:说明: 本程序包括main和被调用 函数max两个函数。max函数的 作用是将x和y中较大者的值赋 给变量z。return语句将z的值 返回给主调函数main。说明:本程序包括main和被调用 函数max两个函数。max函数的 作用是将x和y中较大者的值赋 给变量z。return语句将z的值 返回给主调函数main。int z;if(xy)z=x; else z=y; return (z);1-3 简单的C语言程序介绍C程序:(1) C程序是由函数构成的。 这使得

10、程序容易实现 模块化。(2) 一个函数由两部分组成: 函数的首部:例1.3中的max函数首部int max(int x,int y )函数体:花括号内的部分。若一个函数有多个花 括号,则最外层的一对花括号为函数体的范围。函数体包括两部分 : 声明部分:int a,b,c; 可缺省执行部分:由若干个语句组成。可缺省1-3 简单的C语言程序介绍注意:函数的声明部分和执行部分都可缺省,例如:void dump ( )这是一个空函数,什么也不做,但是合法的函数。1-3 简单的C语言程序介绍小结:(3) C程序总是从main函数开始执行的,与main函数 的位置无关。(4) C程序书写格式自由,一行内可

11、以写几个语句, 一个语句可以分写在多行上,C程序没有行号。(5) 每个语句和数据声明的最后必须有一个分号。(6) C语言本身没有输入输出语句。输入和输出的操 作是由库函数scanf和printf等函数来完成的。C对 输入输出实行“函数化”。骤和方法1-4 运行程序的步一、运行程序的步骤上机输入与编辑源程序对源程序进行编译与库函数连接运行目标程序1-4 运行程序的步骤和方法二、上机运行程序的方法目前使用的大多数C编译系统都是集成环境(IDE)的。可以用不同的编译系统对C程序进行操作常用的有Turbo C 2.0、Turbo C+ 3.0、Visual C+等Turbo C+ 3.0:是一个集成环

12、境,它具有方便、直观和易用的界面,虽然它也是DOS环境下的集成环境, 但是可以把启动Turbo C+ 3.0 集成环境的DOS执行文 件tc.exe生成快捷方式,也可以用鼠标操作。Visual C+:也可以用Visual C+对C程序进行编译。例:Turbo C+ 3.0的使用将Turbo C+ 3.0编译程序装入磁盘某一目录下,例如: 放在C盘根目录下一级TC3.0子目录下。进入Turbo C+ 3.0集成环境在DOS环境下C:TC3.0tc 在Windows环境下找到可执行文件tc.exe,执行该文件。主菜单:11个菜单项:FileEditSearchRunCompileDebugProj

13、ect OptionsWindowHelp(2) 编辑源文件 新建:单击“File”菜单下 的“New”,修改:选择“File”“Open”(即单击“File” 的下拉菜单中 的“Open”项,修改已有的源程序。在编辑(EDIT) 状态下光标表示当前进行编辑的位置,在此位 置可以进行插入、删除或修改,直到自已满意为止。保存:在编辑(EDIT) 状态下光标表示当前进行编辑的位置, 在此位置可以进行插入、删除或修改,直到自已满意为止。(3) 对源程序进行编译选择“Compile”(或“Alt+F9”)对源程序进行编译。c1.cpp源程序,出现1个错误(error) ,0个警告(warming)。(

14、4) 将目标程序进行连接选择菜单“Compile” “Link” ,如果不出现错 误,会得到一个后缀为.exe的可执行文件。 (5) 执行程序。选菜单“Run” “Run”( 或按“Ctrl+F9” 键)。(6) 退出Turbo C+ 3.0环境 选择“File”“Quit” 。第二章本章要点算法的概念算法的表示结构化程序设计方法主要内容2.1算法的概念2.2简单算法举例2.3算法的特性2.4怎样表示一个算法2.5化程序设计方法一个程序应包括两个方面的内容: 对数据的描述:数据结构(data structure)对操作的描述:算法(algorithm)著名计算机科学家沃思提出一个公式: 数据结

15、构 + 算法 = 程序完整的程序设计应该是:数据结构算法程序设计方法语言工具2.1 算法的概念广义地说,为解决一个问题而采取的方 法和步骤,就称为“算法”。对同一个问题,可有不同的解题方法和步骤100例: 求 nn = 1方法1:1+2,+3,+4,一直加到100加99次方法2:100+(1+99)+(2+98)+(49 +51)+50= 100 + 49100 +50加51次2.1 算法的概念为了有效地进行解题,不仅需要保证 算法正确,还要考虑算法的质量,选择合 适的算法。希望方法简单,运算步骤少。计算机算法可分为两大类别: 数值运算算法:求数值解,例如求方程的 根、求函数的定积分等。 非数

16、值运算:包括的面十分广泛,最常见 的是用于事务管理领域,例如图书检索、 人事管理、行车调度管理等。2.2 简单算法举例例2.1:求12345步骤1:先求12,得到结果2 步骤2:将步骤1得到的乘积2再乘以3,得到结果6 步骤3:将6再乘以4,得24 步骤4:将24再乘以5,得120如果要求121000,则要写999个步骤可以设两个变量:一个变量代表被乘数,一个变量代表乘数。不另设变量存放乘积结 果,而直接将每一步骤的乘积放在被乘数 变量中。设p为被乘数,i为乘数。用循环 算法来求结果, 算法可改写:S1:使p=1 S2:使i=2S3:使pi,乘积仍放在变量p中,可表示为:pip S4:使i的值

17、加1,即i+1i。 S5:如果i不大于5,返回重新执行步骤S3以及其后的步骤S4和S5;否则,算法结束。最后得到p的值就 是5!的值。如果题目改为:求1351000算法只需作很少的改动:S1:1p S2:3i S3:pip S4:i+2pS5:若i11,返回S3。否则,结束。用这种方法表示的算法具有通用性、灵活性。S3到S5组成一个循环,在实现 算法时 要反复多次执行S3,S4,S5等步 骤,直到某一时刻,执行S5步骤时经过 判断,乘数i已超过规定的数值而不返回 S3步骤为止。此时算法结束,变量p的值 就是所求结果。例2.2 有50个学生,要求将他们之中成绩在80 分以上者打印出来。设n表示学

18、号, n1代表 第一个学生学号, 代表第i个学生学号。用G 代表学生成绩 , gi代表第i个学生成绩,算法 表示如下:S1:1i S2:如果80,则打印和,否则不打印。 S3:i+1iS4:如果i50,返回S2,继续执行。否则算法结束变量i作为下标,用来控制序号(第几个学 生,第几个成绩)。当i超过50时,表示 已对 50个学生的成绩处理完毕,算法结束。例2.3判定20002500年中的每一年是否闰年,将结果输出。 分析:闰年的条件是:(1)能被4整除,但不能被100整除的年份都是闰年,如1996,2004年是闰 年;(2)能被100整除,又能被400整除的年份是 闰年。如1600,2000年

19、是闰年。不符合这两个条 件的年份不是闰年。变量i作为下标,用来控制序号(第几个学 生,第几个成绩)。当i超过50时,表示 已对 50个学生的成绩处理完毕,算法结束。设y为被检测的年份,算法可表示如下 :S1:2000yS2:若y不能被4整除,则输出y “不是闰年”。然后转 到S6。S3:若y能被4整除,不能被100整除,则输出y “是闰年”。然后转到S6。 S4:若y能被100整除,又能被400整除,输出y“是闰年”,否则输出“不是闰年”。 然后转到S6。S5: 输出y “不是闰年”。 S6:y+1yS7:当y2500时,转S2继续执行,如y2500,算法停止。以上算法中每做一 步都分别分离出

20、一些范围(巳能判定为 闰年或非闰年),逐 步缩小范围,直至 执行S5时,只可能是非闰年。“其它”包括能被4整 除,又能被100整除,而不能被400整除的那些年份(如 1990) 是非闰年。例2.4 求1 12+ 1 31+ .4 199+ 1 100算法如下 :S1:sign=1S2:sum=1 S3:deno=2S4:sign=(-1)sign S5:term=sign(1/deno) S6:sum=sum+term S7:deno=deno+1单词作变量名,以使算 法更易于理解: sum表示累加和,deno是 英文分母(denom inator) 缩写,sign代表数值的符 号,term代

21、表某一项。S8:若deno100返回S4,否则算法结束。 反复执行S4到S8步骤,直到分母大于100为止。一共执行了99次循环,向sum累加入了99个分数。sum最后的值就是多项式的值。例2.5对一个大于或等于3的正整数,判断 它是不是一个素数。概念:所谓素数,是指除了1和该数本身之外, 不能被其它任何整数整除的数。例如,13是 素数。因为它不能被2,3,4,12整除。分析:判断一个数n(n3)是否素数的方法: 将n作为被除数,将2到(n-1)各个整数轮流作 为除数,如果都不能被整除,则n为素数。算法如下 :S1:输入n的值S2:i=2(i作为除数) S3:n被i除,得余数r S4:如果r=0

22、,表示n能被i整除,则打印n“不是素数”,算法结束。否则执行S5S5:i+1iS6:如果in-1,返回S3。否则打印 n “是素数 ”。然后结束。实际上,n不必被2到(n-1)的整数除,只需被2到n/2间整数除,甚至只需被2到 的整数除即可。n 之间2.3 算法的特性一个算法应该具有以下特点:有穷性:包含有限的操作步骤确定性:算法中的每一个步骤都应当是确 定的有零个或多个输入:输入是指在执行算法 时需要从外界取得必要的信息有一个或多个输出:算法的目的是为了求 解,“解”就是输出有效性:算法中的每一个步骤都应当能有 效地执行,并得到确定的结果 。2.4 算法的表示可以用不同的方法表示算法,常用的

23、有: 自然语言 传统流程图 结构化流程图 伪代码 PAD图2.4.1 用自然语言表示算法自然语言就是人们日常使用的语言,可 以是汉语或英语或其它语言。用自然语言 表示通俗易懂,但文字冗长,容易出现“ 歧义性”。自然语言表示的含义往往不大 严格,要根据上下文才能判断其正确含义,描述包含分支和循环的算法时也不很方 便。因此,除了那些很简单的问题外,一般不用自然语言描述算法。2.4.2 用流程图表示算法美国国家标准化协会ANSI(American National Standard Institute)规定了一 些常用的流程图符号:起止框判断框处理框输入/输出框 注释框流向线连接点流程图表示例2.6

24、将求5!的算法用如果需要将最后结果打印出来,可在 菱形框的下面加一 个输出框。例2.7将例2.2的算法用流程图表示。打 印50名 学生中成绩在 80分以上者的学号和 成绩。如果如果包括这个输入数据 的部分,流程 图为用流程图表示算法要比 用文字描述算法逻辑清 晰、易于理解。例2.8将例 2.3判定闰 年的算法用流程图表示例2.9将例2.4的算法用流程1 12+ 1 134+ . 199+ 1100图表示例2.10将例2.5判断素数 的算法用流程图表示小结:流程图是表示算法的较好的工具。 一个流程图包括以下几部分 :(1)表示相应操作的框; (2)带箭头的流程线; (3)框内外必要的文字说明。2

25、.4.3 三种基本结构和改进的流程图1、传统流程图的弊端 传统流程图用流程线指出各框的执行顺序,对流程线的使用没有严格限制。因此,使用者可以毫不受限制地使流程随意 地转向,使流程图变得毫无规律,阅读者 要花很大精力去追踪流程,使人难以理解 算法的逻辑。如图:传统流程缺点:难以阅读、修改,使算法的 可靠性和可维护性难以保证。 解决办法:必须限制箭头的滥用,图的流程可以是:即不允许无规律地使流程随意转 向,只能顺序地进行下去。这种如同乱麻一样的算法称为BS型算法,意为一碗面条(A Bowl of Spaghetti),乱无头绪。2、三种基本结构Bohra和Jacopini提出了以下三种基本 结构:

26、顺序结构、选择结构、循环结构 用这三种基本结构作为表示一个良好算法的基本单元。三种基本结构的图示:顺序结构选择结构循环结构的图示:当型(While型)循环结构直到型(Until型)循环三种基本结构的共同特点: (1)只有一个入口; (2)只有一个出口;(请注意:一个菱形判断框有两个出口,而一个选择结构只有一个出口。不要将菱形框的出口和选择结构的出口 混淆。)(3)结构内的每一部分都有机会被执行到; (4)结构内不存在“死循环”(无终止的循环)。不正确的流程表示:图中没有一条从入口到出口的 路径通过A框。流程内的死循环小结:由三种基本结构顺序组成的算法结构,可以解决任何复杂的问题。由基本 结构所

27、构成的算法属于“结构化”的算 法,它不存在无规律的转向,只在本 基本结构内才允许存在分支和向前或向后的跳转。扩展: 只要具有上述四 个特点的都可以 作为基本结构。 可以自己定义基 本结构,并由这 些基本结构组成结构化程序。此图符合基本结构的特点这是一个多分 支选择结构,根据 表达式的值决定执 行路线。虚线框内 的结构是一个入口一个出口,并且有 上述全部的四个特 点。由此构成的算 法结构也是结构化 的算法。可以认为 这是由三种基本结构所派生出来的。2.4.4 用N-S流程图表示算法1973年美国学者I.Nassi和B.Shneiderman 提出了一种新的流程图形式。在这种流程图 中,完全去掉了

28、带箭头的流程线。全部算法 写在一个矩形框内,在该框内还可以包含其 它的从属于它的框,或者说,由一些基本的 框组成一个大的框。这种流程图又称N-S结 构化流程图 。N-S流程图用以下的流程图符号:(1)顺序结构(2)选择结构(3)循环结构用三种N-S流程图中的基本框,可以组成复 杂的N-S流程图。图中的A框或B框,可以是一个简单的操作,也可以是三个基本结构之一。A框可以是一个选择结构 B框可以是一个循环结构 例2.11 将例2.1 的求5!算法用 N -S图表示例2.12将例2.2 的算法用N -S图表示。( 打印50名学 生中成绩高 于80分的学 号和成绩)没有输入数据例2.12将例2.2 的

29、算法用N -S图表示。( 打印50名学 生中成绩高 于80分的学 号和成绩)有输入数据例2.13将例2.3 判定闰 年的算 法用N - S图表示例2.14 将例2.4的算 法用N -S图表示1 12+ 1 134+ . 199+ 1100出口1出口2例2.15 将例2.5判别 素数的算法用N -S 流程图表示。传统流程图分析:此图不符合基本结构特点! 由于不能分解为三种基本结 构,就无法直接用N-S流程 图的三种基本结构的符号来 表示。因此,应当先作必要 的变换。例2.15 将例2.5判别 素数的算法用N -S流程图表示。传统流程图变换为:一个出口用流 程 图 表 示:N -SN-S图表示算法

30、的优点比文字描述直观、形象、 易于理解; 比传统流程图紧凑易画。尤其是它废除 了流程线,整个算法结构是由各个基本 结构按顺序组成的,N-S流程图中的上 下顺序就是执行时的顺序。用N-S图表 示的算法都是结构化的算法,因为它不 可能出现流程无规律的跳转,而只能自 上而下地顺序执行。小结:一个结构化的算法是由一些基本结构顺序 组成的。在基本结构之间不存在向前或向 后的跳转,流程的转移只存在于一个基本 结构范围之内(如循环中流程的跳转);一 个非结构化的算法可以用一个等价的结构 化算法代替,其功能不变 。如果一个算 法不能分解为若干个基本结构,则它必然 不是一个结构化的算法。2.4.5 用位代码表示

31、算法概念:伪代码是用介于自然语言和计算机 语言之间的文字和符号来描述算法。特点:它如同一篇文章一样 ,自上而下地 写下来。每一行(或几行)表示一个基本操 作。它不用图形符号,因此书写方便 、格 式紧凑,也比较好懂,也便于向计算机语 言算法(即程序)过渡。用处:适用于设计过程中需要反复修改时 的流程描述。IF x is positive THEN print xELSEprint -x也可以用汉字伪代码表示:若 x为正 打印 x 否则 打印 -x也可以中英文混用,如:IF x 为正print x ELSEprint -x例: “打印x的绝对值” 的算法可以用伪代码 表示为:开始置t的初值为1置i

32、的初值为2当i=5,执行下面操作:也可以写成以下形式:使t=tiBEGIN算法开始使i=i+11t循环体到此结束2 i输出t的值while i5结束ti ti+1 i print tEND算法结束例2.16求5!。用伪代码表示算法:例2.17 输出50个学生中成绩高于80分者的学号和成绩。用伪代码表示算法:BEGIN算法开始 1 iwhile i50inputand i+1 i1 i while i50if 80 printandi+1 i END算法结束2.4.6 用计算机语言表示算法 概念:用计算机实现算法。计算机是无法 识别流程图和伪代码的。只有用计算机语 言编写的程序才能被计算机执行。

33、因此在 用流程图或伪代码描述出一个算法后,还 要将它转换成计算机语言程序。 特点:用计算机语言表示算法必须严格遵 循所用的语言的语法规则,这是和伪代码 不同的。 用处:要完成一件工作,包括设计算法和 实现算法两个部分。设计算法的目的是为 了实现算法。#include void main( )int i,t; t=1; i=2;while(i=5)t=t*I; i=i+1;printf(“%dn”,t);例 2.20 将例2.16表示 的算法(求5!)用 C语言表示。应当强调说明:写出了C程序,仍然只是描述了算法,并未实现算法。只 有运行程序才是实现算法。应该说, 用计算机语言表示的算法是计算机

34、能 够执行的算法。 2.5结构化程序设计方法一个结构化程序 就是用高级语言表示的结构 化算法。用三种基本结构组成的程序必然是 结构化的程序,这种程序便于编写、便于阅 读、便于修改和维护。结构化程序设计强调程序设计风格和程序结 构的规范化,提倡清晰的结构。结构化程序设计方法的基本思路是:把一个 复杂问题的求解过程 分阶段进行,每个阶段 处理的问题都控制在人们容易理解和处理的 范围内。 2.5结构化程序设计方法采取以下方法来保证得到结构化的程序:自顶向下;逐步细化;模块化设计;两种不同的方法:自顶向下,逐步细化;自下而上,逐步积累。结构化编码。用这种方法逐步分解,直到作者认为可以直接将 各小段表达

35、为文字语句为止。这种方法就叫 做“自顶 向下,逐步细化”。自顶向下,逐步细化方法的优点: 考虑周全,结构清晰,层次分明,作者容易写,读者容易看。如果发现某一部 分中有一段内容不妥,需要修改,只需找 出该部分修改有关段落即可,与其它部分 无关。我们提倡用这种方法设计程序。这就是用工程的方法设计程序。模块设计的方法:模块化设计的思想实际上是一种“分而治之” 的思想,把一个大任务分为若干个子任务, 每一个子任务就相对简单了。在拿到一个程序模块以后,根据程序模块的 功能将它划分为若干个子模块,如果这些子 模块的规模还嫌大,还再可以划分为更小的 模块。这个过程采用自顶向下方法来实现。子模块一般不超过50

36、行划分子模块时应注意模块的独立性,即:使 一个模块完成一项功能,耦合性愈少愈好。第三章本章要点数据的描述规则数据的操作规则主要内容3.1C的数据类型3.2常量与变量3.3整型数据3.4浮点型数据运行3.5字符型数据主要内容3.变量赋初值3.7各类数值型数据间的混合运 算3.8算术运算符和算术表达式3.9赋值运算符和赋值表达式3.10逗号运算符和逗号表达式 3.1C的数据类型C语言提供了以下一些数据类型。整型int基本类型数据类型构造类型指针类型字符型char 实型(浮点型) 数组类型结构类型 struct 联合类型 union 枚举类型enum单精度实型float双精度实型double空类型(

37、无值类型)void3.2 常量与变量3.2.1常量和符号常量在程序运行过程中,其值不能被改变的量称为常量常量区分为不同的类型:整型 100,125,-100,0实型 3.14 , 0.125,-3.789字符型 a,b,2字符串 a,ab,1232运行结果: total=300例3.1 符号常量的使用#definePRICE30#include void main ( )int num, total; num=10;total=num * PRICE;说明:说明:说明:说明:程序中用#define命令行定义PRICE 代表常量30,此后凡在本文件中出现的 PRICE都代表30,可以和常量一样进

38、行运算说明:说明:说明:说明:用一个标识符代表一个常量的,称为符 说明:说明:说明:说明:说明:说明:说明:说明:程序中用#define命令行定义PRICE 号常量,即以标识符形式出现的常量。符号代表常量常量的值在其作用域30,此后凡在本文件中出现的(在本例中为主函数) PRICE内不能改变都代表,30,也不能再被赋值。可以和常量一样进行运算说明:说明:说明:说明:说明:说明:说明:说明:说明:说明:说明:说明:用一个标识符代表一个常量的如再用赋值语句给PRICE赋值是错误,称为符 的。 号常量,即以标识符形式出现的常量。符号PRICE=40;/* 错误,不能给符 常量的值在其作用域(在本例中

39、为主函数) 号常量赋值。 内不能改变,也不能再被赋值。printf(total=%dn,total);说明:如再用赋值语句给PRICE赋值是错误 的。PRICE=40;/* 错误,不能给符 号常量赋值。图3.2 常量与变量属性的一个存储单 就是变量的值,在 可以改变的。个名字对应代表一 接时由编译系统给 内存地址。从变量 量名找到相应的内 读取数据。图3.2.2变量 变量代表内存中具有特定 元,它用来存放数据,这 程序运行期间,这些值是 变量名实际上是一个以一 个地址,在对程序编译连 每一个变量名分配对应的 中取值,实际上是通过变 存地址,从该存储单元中3.2 常量与变量变量命名的规定:语言规

40、定标识符只能由 字母、数字和下划线三种字符组成,且第一 个字符必须为字母或下划线。例:sum,_total, month, Student_name,lotus_1_2_,BASIC, li_lingM.D.John, ¥123,3D64,ab3.2 常量与变量注意:编译系统将大写字母和小写字母认为是两 个不同的字符。建议变量名的长度最好不要超过8个字符。在选择变量名和其它标识符时,应注意做 到“见名知意”,即选有含意的英文单词(或其缩写)作标识符。要求对所有用到的变量作强制定义,也就 是“先定义,后使用”。3.3整型数据3.3.1整型常量的表示方法 整型常量即整常数。在语言中,整常数可用以下

41、三种形式表示:(1)十进制整数。如:123, -456.4。(2)八进制整数。以0头的数是八进制数。 如:0123表示八进制数123,等于十进制数83,-011表示八进制数-11,即十进制数-9。数是16进制数。 23,等于十进制数。放形式式存放的。 整型变量 */ 以整数10 图*/图3.3整型数据(3)十六进制整数。以0x开头的 如:0x123,代表16进制数1291。 -0x12等于十进制数103.3.2 整型变量 (1)整型数据在内存中的存数据在内存中是以二进制形如: int i;/* 定义为i=10;/* 给i赋3.3整型数据注意:十进制数10的二进制形式为1010,TurboC 2

42、.0和Turbo C+ 3.0为一个整型变量在 内存中分配2个字节的存储单元(不同的编 译系统为整型数据分配的字节数是不相同 的,VC+ 6.0则分配4个字节)。数值是以补码(complement) 表示的。3.3整型数据(2)整型变量的分类:有符号基本整型 有符号短整型(signed)int (signed)short (int )共六种有符号长整型无符号基本整型 无符号短整型 无符号长整型(signed) long (int) unsigned int unsigned short (int) unsigned long (int)注意:括号表示其中的内容是可选的.3.3整型数据整数类型的

43、有关数据:类型类型说明符长度数的范围 基本型int2字节-3276832767短整型short2字节-215215-1长整型long4字节-231231-1 无符号整型unsigned2字节065535 无符号短整型 unsigned short 2字节065535 无符号长整型 unsigned long4字节0(232-1)3.3整型数据(3)整型变量的定义:规定在程序中所有用到的变量都必须在 程序中定义,即“强制类型定义”。例如:inta,b(指定变量、为整型)unsignedshortc,d;(指定变量、为无 符号短整型)longe,f;(指定变量、为长整型)例3.2 整型变量的定义#

44、include void main()运行结果: 与使用,int a,b,c,d;/*指定、为整 型变量*unsigned ; *指定为无符号整型变量*;(说明:说明:说明:说明:,可以看到不同种类的整型数据可以);进行算术运算12;-24;10; printf,说明:可以看到不同种类的整型数据可以进行算术运算例3.3 整型数据的溢出#include voidmain()int a,b; a=32767;(“%d,%dn”,a,b);说明:说明:说明:说明:数值是以补码表示的。一个整型变量只能容纳-3276832767范围内的数,无法 表示大于32767或小于-32768的数。遇此情 况就发生

45、“溢出”。b=a+1; printf运行结果: 32767,-32768说明:数值是以补码表示的。一个整型变量只能容纳-3276832767范围内的数,无法表示大于32767或小于-32768的数。遇此情 况就发生“溢出”。3.3整型数据3.3.3整型常量的类型(1)一个整数,如果其值在-32768+32767范 围内,认为它是int型,它可以赋值给int型 和long int型变量。(2) 一个整数,如果其值超过了上述范围, 而在-2147483637+2147483647范围内,则认为它是为长整型。可以将它赋值给一个 long int型变量。3.3整型数据(3) 如果所用的C版本(如Tur

46、bo C)分配给 short int与int型数据在内存中占据的长度 相同,则它的表数范围与int型相同。因此 一个int型的常量同时也是一个short int型 常量,可以赋给int型或short int型变量。3.3整型数据(4) 一个整常量后面加一个字母u或U,认 为是unsigned int型,如12345u,在内存 中按unsigned int规定的方式存放(存储 单元中最高位不作为符号位,而用来存储 数据)。如果写成-12345u,则先将-12345 转换成其补码53191,然后按无符号数存 储。3.3整型数据(5) 在一个整常量后面加一个字母l或L,则 认为是long int型常

47、量。例如123l.432L.0L 等。这往往用于函数调用中。如果函数的形 参为long int型,则要求实参也为long int 型。3.4 浮点型数据3.4.1浮点型常量的表示方法两种表 示形式小数0.123指数3e-3注意:字母e(或E)之前必须有数字,且e后面的指数必须为整数 1e3、1.8e-3、-123e-6、-.1e-3 e3、2.1e3.5、.e3、e3.4 浮点型数据规范化的指数形式:在字母e(或E)之前的小数部分中,小数点左边 应有一位(且只能有一位)非零的数字.例如:123.456可以表示为:123.456e0,12.3456e1,1.23456e2,0.123456e3,

48、0.0123456e4,0.00123456e其中的1.23456e3称为“规范化的指数形式”。放形式 内存中占4个字存储方式不同, 式存储的。系统 数部分和指数部采用规范化的指图图3.4 浮点型数据3.4.2 浮点型变量 (1)浮点型数据在内存中的存一个浮点型数据一般在 节(32位)。与整型数据的 浮点型数据是按照指数形 把一个浮点型数据分成小 分,分别存放。指数部分 数形式。3.4 浮点型数据(2)浮点型变量的分类浮点型变量分为单精度(float型)、双精度(double型)和长双精度型(long double)三类 形式。类型位数数的范围有效数字float3210-37 103867 位

49、double型6410-307103081516位long double12810-49311049321819位例3.4 浮点型数据的#include void main()float a,b;a = 123456.789e5; b = a + 20 ;运行结果: 123456.789e5说明:说明:说明:说明:一个浮点型变量只能保证的有效数字是7位有效数字,后面的数字是无意义的,并不准确地表示该数。应当避免将一个很大的数和一个很小 的数直接相加或相减,否则就会“丢失”小的数舍入误差printf(“%fn”,b);说明:一个浮点型变量只能保证的有效数字是7位有效数字,后面的数字是无意义的,并

50、不准确地 表示该数。应当避免将一个很大的数和一个很小 的数直接相加或相减,否则就会“丢失”小的数3.4 浮点型数据3.4.3 浮点型常量的类型 C编译系统将浮点型常量作为双精度来处理。例如:f = 2.45678 * 4523.65系统先把2.45678和4523.65作为双精度数,然后 进行相乘的运算,得到的乘也是一个双精度数。最 后取其前7位赋给浮点型变量f。如是在数的后面加 字母f或F(如1.65f, 654.87F),这样编译系统就 会把它们按单精度(32位)处理。3.5字符型数据3.5.1 字符常量 (1)用单引号包含的一个字符是字符型常量 (2)只能包含一个字符a,A,1abc、“

51、a”r3.5字符型数据有些以“”开头的特殊字符称为转义字符n换行t横向跳格r回车反斜杠dddddd表示1到3位八进制数字xhh hh表示1到2位十六进制数字例3.5 转义字符的#include 打印机上的显示结果:打印机上的显示结果:打印机上的显示结果:打印机上的显示结果:ffffaaaabbbb ccccggggddddeeee hhhhjjjjiiiikkkkfgdej k( ab ctderftgn);printf(htibbj kn);3.5字符型数据3.5.2字符变量字符型变量用来存放字符常量,注意只能放 一个字符。字符变量的定义形式如下:char c1,c2;在本函数中可以用下面语

52、句对c1,c2赋值: c1a;c2 b;一个字符变量在内存中占一个字节。式及其使用方法 变量中,实际上并 元中去,而是将该 存储单元中。图图3.5字符型数据3.5.3字符数据在内存中的存储形 将一个字符常量放到一个字符 不是把该字符本身放到内存单 字符的相应的ASCII代码放到这样使字符型数据和整型数据之间可以通用。一个字符数据 既可以以字符形式输出,也可 以以整数形式输出。例3.6 向字符变量赋#include void main()char c1,c2; c1=97; c2=98;运行结果:以整数。 9798说明:说明:说明:在第和第4行中,将整数和分说明:printf(“%d %dn”,

53、c1,c2);别赋给c1和c2,它的作用相当于以下两个赋值语句:c1;c2;因为a和b的ASCII码为和printf(“%c %cn”,c1,c2);说明:在第和第4行中,将整数和分别赋给c1和c2,它的作用相当于以下两个赋值语 句:c1;c2; 因为a和b的ASCII码为和运行结果:例3.7 大小写字母的转换#include void main()char c1,c2; c1=a;说明:说明:说明:说明:程序的作用是将两个小写字母a和b转换成c2大写字母-32;A和B。从代码表中可以看到 p每一个小写字母比它相应的大写字母的rintf(“c c,c1,c2);码大。语言允许字符数据与整数直接

54、进行算术运算。c2=b; c1=c1-32;c2=说明:程序的作用是将两个小写字母a和b转换成大写字母A和B。从代码表中可以看到 每一个小写字母比它相应的大写字母的 码大。语言允许字符数据与整数直接进 行算术运算。3.5字符型数据说明:说明:说明:说明:有些系统(如Turbo C)将字符变量定义为signed char型。其存储单元中的最高位作为符号位,它的取 值范围是-128127。如果在字符变量中存放一个 ASCII码为0127间的字符,由于字节中最高位为0, 因此用%d输出字符变量时,输出的是一个正整数。如 果在字符变量中存放一个ASCII码为128255间的字 符,由于在字节中最高位为

55、1,用%d格式符输出时, 就会得到一个负整数。说明:有些系统(如Turbo C)将字符变量定义为signed char型。其存储单元中的最高位作为符号位,它的取 值范围是-128127。如果在字符变量中存放一个 ASCII码为0127间的字符,由于字节中最高位为0, 因此用%d输出字符变量时,输出的是一个正整数。如 果在字符变量中存放一个ASCII码为128255间的字 符,由于在字节中最高位为1,用%d格式符输出时, 就会得到一个负整数。3.5字符型数据3.5.4字符串常量字符串常量是一对双撇号括起来的字符序列.合法的字符串常量:“How do you do.”, “CHINA”, “a”,

56、“$123.45”可以输出一个字符串,如 printf(“How do you do.”);3.5字符型数据是字符常量,”是字符串常量,二者不 同。如:假设被指定为字符变量 :char cc=a;”a”;c”CHINA”;结论:不能把一个字符串常量赋给一个字符变量。3.5字符型数据规定:在每一个字符串常量的结尾加一个 “字符 串结束标志”,以便系统据此判断字符串是否结束。 规定以字符作为字符串结束标志。CHINA0如:如果有一个字符串常量” ,实际上在内存中是:它占内存单元不是个字符,而是个字符, 最后一个字符为。但在输出时不输出。3.6变量赋初值字符串常量(1)语言允许在定义变量的同时使变量

57、初始化 如:int a=3; / 指定为整型变量,初值为float f=3.56; / 指定为浮点型变量,初值 为.56char c= a; / 指定为字符变量,初值 为a3.6变量赋初值(2)可以使被定义的变量的一部分赋初值。如:int a,b,c=5;表示指定、为整型 变量,但只对初始化,c的初值为(3)如果对几个变量赋以同一个初值,应写成:int a=3,b=3,c=3;表示、 的初值都是。不能写成int a=b=c3; 注意:初始化不是在编译阶段完成的而是在程序运 行时执行本函数时赋初值的,相当于有一个赋值语 句。型数据间的混合运算t,long)、浮点型(包括 混合运算。在进行运算时

58、先转换成同一类型,然后图图3.7各类数值整型(包括int,shor float,double)可以,不同类型的数据要 进行运算. 上述的类型转换是由 系统自动进行的3.8算术运算符和算术表达式3.8.1 运算符简介的运算符有以下几类: (1)算术运算符 (+ - * / %)(2)关系运算符 (!) (3)逻辑运算符 (!|)(4)位运算符( |) (5)赋值运算符 (及其扩展赋值运算符) (6)条件运算符 (?:)(7)逗号运算符(,)3.8算术运算符和算术表达式(8)指针运算符 (*和) (9)求字节数运算符() (10)强制类型转换运算符( (类型) ) (11)分量运算符(-) (12

59、)下标运算符()(13)其他 (如函数调用运算符()3.8算术运算符和算术表达式3.8.2 算术运算符和算术表达式 (1)基本的算术运算符:(加法运算符,或正值运算符。如:、)(减法运算符,或负值运算符。如:、)*(乘法运算符。如:*)(除法运算符。如:)(模运算符,或称求余运算符,两侧均应为整型 数据,如:的值为)。3.8算术运算符和算术表达式(2)算术表达式和运算符的优先级与结合性基 本的算术运算符:用算术运算符和括号将运算对象(也称操作数) 连接起来的、符合语法规则的式子,称为算 术表达式。运算对象包括常量、变量、函数等。例如:*.5a 是一个合法的表达式3.2 常量与变量语言规定了运算

60、符的优先级和结合性。在表达式求值时,先按运算符的优先级别高低 次序执行,例如先乘除后加减。规定了各种运算符的结合方向(结合性)算术运算符的结合方向为“自左至右”,即先左 后右 。3.8算术运算符和算术表达式(3)强制类型转换运算符可以利用强制类型转换运算符将一个表达式转换成 所需类型。一般形式:(类型名)(表达式) 例如: (double)将转换成double类型(int)(x+y)将x+y的值转换成整型(float)(5%3)将5%3的值转换成float型例3.8 强制类型转换。 运行结果:#include void()x3.600000, i=3说明:说明:说明:说明:pri有两种类型转换

61、,一种是在运算时不必用ntf(x=%f, i=%dn,x,i);户指定,系统自动进行的类型转换,如3+6.5。 第二种是强制类型转换。当自动类型转换不能实 现目的时,可以用强制类型转换。 ; ; ; ();说明:有两种类型转换,一种是在运算时不必用户指定,系统自动进行的类型转换,如3+6.5。 第二种是强制类型转换。当自动类型转换不能实 现目的时,可以用强制类型转换。3.8算术运算符和算术表达式(4) 自增、自减运算符作用是使变量的值增或减如:,(在使用之前,先使的值加(减),(在使用之后,使的值加( 减)3.8算术运算符和算术表达式i+与+i的区别:是先执行后,再使用的值; 是先使用的值后,

62、再执行。例如:;i的值先变成4, 再赋给,j的值均为;先将 i的值3赋给,的值为,然后变为3.8算术运算符和算术表达式注意:(1)自增运算符(),自减运算符(), 只能用于变量,而不能用于常量或表达式,(2)和的结合方向是“自右至左”。自增(减)运算符常用于循环语句中使循环变量 自动加。也用于指针变量,使指针指向下一个地址3.8算术运算符和算术表达式(5)有关表达式使用中的问题说明ANSI C并没有具体规定表达式中的子表达式的求 值顺序,允许各编译系统自己安排。例如:对表达式a = f1( )+f2( ) 并不是所有的编译系统都先调用f1( ), 然后调用f2( )。在有的情况下结果可能不同。

63、有时会出 现一些令人容易搞混的问题,因此务必要小心谨慎。3.8算术运算符和算术表达式语言中有的运算符为一个字符,有的运算符由 两个字符组成 ,为避免误解,最好采取大家都能理 解的写法。例如:不要写成i+j的形式,而应写成(i+)+j的形式3.8算术运算符和算术表达式在调用函数时,实参数的求值顺序,标准并无统 一规定。 例如:的初值为,如果有下面的函数调用:printf(,i+) 在有的系统改写成j,i)中,从左至右求值,输出“,”。在多数系 统中对函数参数的求值顺序是自右而左, 函数输出的是“,”。以上这种写法不宜提倡, 最好j=i+;不要写出别人看不懂的也不知道系统会怎样执行程printf(

64、%d,%d, 序3.9 赋值运算符和赋值表达式(1)赋值运算符赋值符号“”就是赋值运算符,它的作用是 将一个数据赋给一个变量。如“”的作用 是执行一次赋值操作(或称赋值运算)。把常 量赋给变量。也可以将一个表达式的值赋 给一个变量.3.9 赋值运算符和赋值表达式(2)类型转换 如果赋值运算符两侧的类型不一致,但都是数值型或字符型时,在赋值时要进行类型 转换。将浮点型数据(包括单、双精度)赋给整型变量时,舍弃浮点数的小数部分。 如:为整型变量,执行“i=3.56”的结果是使 的值为,以整数形式存储在整型变量中.3.9 赋值运算符和赋值表达式将整型数据赋给单、双精度变量时,数值不变,但以浮点数形式

65、存储到变量中如: 将赋给float变量,即执行,先将转换成,再存储在中。 将赋给型变量,即执行,则将补足有效位数字为,然后以双精度浮点数形式存储 到变量中。3.9 赋值运算符和赋值表达式将一个double型数据赋给float变量时,截取其前面7位有效数字,存放到float变量的存储单元(4个 字节)中。但应注意数值范围不能溢出。如:float f;double d=123.456789e100;f=d; 就出现溢出的错误。如果将一个float型数据赋给double变量时,数值不 变,有效位数扩展到16位,在内存中以8个字节存储达式于字符只占1个字将字符数据(个 低位中。的字符类型,或程 低位,

66、高位补型变量图图3.9 赋值运算符和赋值表字符型数据赋给整型变量时,由节,而整型变量为个字节,因此 二进位)放到整型变量存储单元的第一种情况: 如果所用系统将字符处理为无符号 序已将字符变量定义为 型,则将字符的位放到整型变量零 例如:将字符赋给3.9赋赋值值运运算算符符和和赋赋值值表表达达达式式字符处理为带符号 高位为,则整型 ,则高位全补 目的是使数值保持以整数形式输出图第二种情况:如果所用系统(如Turbo C+)将的(即signed char),若字符最 变量高位补;若字符最高位为 。这称为“符号扩展”,这样做的 不变,如变量(字符) 为,的值也是。图3.9赋赋值值运运算算符符和和赋赋

67、值值表表达达达式式赋给一个char型变char型变量(即图将一个int、short、long型数据量时,只将其低8位原封不动地送到 截断)。例如:int i=289;char c=a;c=i;赋值情况 :c的值为33, 如果用“%c”输出c,将得到字符“!” (其 ASCII码为33)。图3.9 赋值运算符和赋值表达式将带符号的整型数据(int型)赋给long型变 量时,要进行符号扩展,将整型数的16位送到 long型低16位中:如果int型数据为正值(符号位为),则long 型变量的高16位补;如果int型变量为负值(符号位为),则long 型变量的高16位补,以保持数值不改变。反之,若将一

68、个long型数据赋给一个int型变量,只将long型数据中低16位原封不动地送到整型 变量(即截断)。3.9赋赋值值运运算算符符和和赋赋值值表表达达达式式图图图3.14图3.14例如:int a;long b;a=b赋值情况如图 :如果(八进制数),则 赋值后值为。见图 3.143.9 赋值运算符和赋值表达式将unsigned int型数据赋给long int型变量时,不存在符号扩展问题,只需将高位补即可。将一个 unsigned类型数据赋给一个占字节数相同的非 unsigned型整型变量(例如:unsigned int-int,unsigned long-long,unsigned shor

69、t-short),将unsigned型变量的内容原样送到非 unsigned型变量中,但如果数据范围超过相应整型的 范围,则会出现数据错误。达式b;;图图3.9 赋值运算符和赋值表例如:unsigned int 65535;int将整个送到中,由于是int, 第位是符号位,因此b成了 负数。根据补码知识可知,的 值为-1,可以用printf(%d,);来验证。将非unsigned型数据赋给长度 相同的unsigned型变量,也 是原样照赋(连原有的符号 位也作为数值一起传送)。例3.9 有符号数据传送给#include void () ; (运行结果:;n,)图无符号变量。说明:说明:说明:说

70、明:“%u”是输出无符号数时所用的格式符。如果为正 值,且在32767之间,则赋值后数值不变。赋值情况见图; 说明:“%u”是输出无符号数时所用的格式符。如果为正 值,且在32767之间,则赋值后数值不变。赋值情况见图图3.9 赋值运算符和赋值表达式(3)复合的赋值运算符在赋值符“”之前加上其他以运“算符,可”为以例构来成说明复,合的运算符。例如:它相当于使进行一次自加()的操作。即先使加 ,再赋给。等价于*等价于*( )等价于3.9 赋值运算符和赋值表达式为便于记忆,可以这样理解: a += b(其中a为变量,b为表达式) a += b(将有下划线的“a+”移到“=”右侧)| a = a +

71、 b (在“=”左侧补上变量名a)3.9 赋值运算符和赋值表达式注意:如果是包含若干项的表达式,则相当于它有凡是二元(二目)运括号。如: x %= y+3 x %= (y+3)算符,都可以与赋值 符一起组合成复合赋 值符。| x = x %(y+3)(不要错写成x=x%y+3)语言规定可以使用种复合赋值运算符:,*,|3.9 赋值运算符和赋值表达式(4)赋值表达式 由赋值运算符将一个变量和一个表达式连接起来的式子称为“赋值表达式”。 一般形式为:例如:“5”是一个赋值表达式3.9 赋值运算符和赋值表达式对赋值表达式求解的过程是:求赋值运算符右侧的“表达式”的值;赋给赋值运算符左侧的变量。例如:

72、 赋值表达式“=3*5”的值为15,执行表 达式后,变量a的值也是15。注意:一个表达式应该有一个值3.9 赋值运算符和赋值表达式左值(lvalue) : 赋值运算符左侧的标识符变量可以作为左值;而表达式就不能作为左值(如a+b);常变量也不能作为左值,右值(lvalue) :出现在赋值运算符右侧的表达式 左值也可以出现在赋值运算符右侧,因而左值都可以作为右值。3.9 赋值运算符和赋值表达式赋值表达式中的“表达式”,又可以是一个赋值表达式.例如:a=(b=5) 分析:括弧内的“b=5”是一个赋值表达式,它 的值等于5。执行表达式“a=(b=5)”相当于执行 “b=5”和“ab”两个赋值表达式。

73、 赋值运算符 按照“自右而左”的结合顺序,因此,“(b5)” 外面的括弧可以不要,即“a=(b=5)”和“a=b=5” 等价.3.9 赋值运算符和赋值表达式请分析下面的赋值表达式(a=3*5)=4*3分析:先执行括弧内的运算,将15赋给a,然后执行4*3的运算,得12,再把12赋给a。最后a的值为12, 整个表达式的值为12。可以看到(a=3*5)出现在赋不能写成:值运算符的左侧,因此赋值表达式(a=3a*=53)*是5=4左*3值注意:在对赋值表达式(a=3*5)求解后,变量a得到值 15执行(a=3*5)=4*3时,实际上是将4*3的积12赋给变 量a,而不是赋给3*5。3.9 赋值运算符

74、和赋值表达式赋值表达式也可以包含复合的赋值运算符。 如:a+=a-=a*a分析:此赋值表达式的求解步骤如下 先进行“*”的运算, 它相当于 *,a的值为144132。再进行“”的运算,相当于 =a+(-132),a的值为132-132- 264。3.9 赋值运算符和赋值表达式将赋值表达式作为表达式的一种,使赋值操作不 仅可以出现在赋值语句中,而且可以以表达式形 式出现在其他语句(如输出语句、循环语句等) 中.如:printf(%d,a=b); 分析:如果b的值为3, 则输出a的值(也是表达式a=b的值)为3。在一个语句中完成 了赋值和输出双重功能。3.10 逗号运算符和逗号表达式逗号运算符:将

75、两个表达式连接起来逗,又号称表为达式“的顺序求值运算符”如:5,值为14一般形式:表达式,表达式求解过程: 先求解表达式,再求解表达式。整个逗号表达式的值是表达式的值。3.10 逗号运算符和逗号表达式的值为5,例:逗号表达式*5,*然后求解* ,得。整分析:赋值运算符的优先级别高于逗号运个逗算号符表,达式因的此应先求解*5.值为。一个逗号表达式又可以与另一个表达式组成一个新的逗号表达式 如:(*,*)先计算出的值等于,再进行*的 运算得60(但值未变,仍为15),再进行得,即整个表达式的值为。3.10 逗号运算符和逗号表达式逗号表达式的一般形式可以扩展为表达式,表达式,表达式,表达式赋值表达式

76、,它的值为表达式的值。将一个逗号表逗号运算符是所有运算符中级别最低的达式的值赋给,的值等例: (,*) ,*3于逗号表达式,包括一 个赋值表达式和一个 算术表达式,的值 为,整个逗号表达 式的值为18。3.10 逗号运算符和逗号表达式“,”并不是一个逗号注意:并不是任何地方出现的逗号都表是达作式,为它逗是号运算符。例如函数参数也是p用rin逗tf函号数来的3间个隔的。如:printf(“%d,%d,%d”,a,b,c);printf(“%d,%d,%d”,(a,b,c),b,“c()参数,)”是一个逗号表达式,它 的值等于的 值。第四章主要内容4.1C语句概述4.2赋值语句4.3数据输入输出的

77、概念及在c语言中的实 现4.4字符数据的输入输出4.5格式输入与输出4.6顺序结构程序设计举例 4.1C语句概述C程序源程序文件1源程序文件2源程序文件n预处理命令数据声明函数1函数n函数首部函数体数据声明执行语句 4.1C语句概述(续)一个c程序可以有若干个源程序文件组成一个源文件可以有若干个函数和预处理命令 以及全局变量声明部分组成一个函数有函数首部和函数体组成函数体由数据声明和执行语句组成C语句分为 5类控制语句 函数调用语句表达式语句 空语句 复合语句 4.1C语句概述(续)(一)控制语句完成一定的控制功能1if() else 条件语句2for() 循环语句3while()循环语句4d

78、o while();循环语句5 continue 继续语句6 break间断语句7 switch() 开关语句8 goto转向语句9 return返回语句 4.1C语句概述(续)(二)函数调用语句 有一个函数调用加一个分号构成一个语句Printf(“This is a C statement.”);表达式语句(三)表达式语句有一个表达式加一个分号构成一个语句赋值表达式分号a=3; 4.1C语句概述(续) 4.1C语句概述(续)(四)空语句只有一个分号的语句(什么也不做)用来做流程的转向点用来作为循环语句中的循环体 4.1C语句概述(续)(五)复合语句用一对括起来的语句z=x+y;t=z/100

79、; printf(“%f”,t); 4.2赋值语句赋值语句是由赋值表达式加上一个分号构成 例:a=100赋值表达式a=100;赋值语句例:if(a=b)t=a;错误if(a=b)0)t=a;正确条件中不能含有赋值符号,但是赋值表达式可以 包含于条件表达式中 4.2赋值语句(续)问题:c语言中的赋值语句于其他高级语言 的赋值语句有什么不同点?1111:C语言中的赋值号“=”是一个运算符,在其他大多数语言中赋值号不是运算符.222:其他大多数高级语言没有“赋值表达式 ”这一概念.1:C语言中的赋值号“=”是一个运算符, 在其他大多数语言中赋值号不是运算符.2:其他大多数高级语言没有“赋值表达式 ”

80、这一概念. 4.3数据输入输出的概念及在C语言中的实现(一).所谓输入输出是以计算机主机为主体而言的输出:从计算机向外部输出设备(显示器,打印机) 输出数据输入:从输入设备(键盘,鼠标,扫描仪)向计算机 输入数据. 4.3 数据输入输出的概念及在C语言中的实现(二).C语言本身不提供输入输出语句,输入和输出 操作是由C函数库中的函数来实现的例如:字符输入函数: getchar 字符输出函数:putchar 格式输入函数: scanf 格式输出函数: printf 字符串输入函数:gets 字数穿输出函数:puts 4.3数据输入输出的概念及在C语言中的实现(三).在使用系统库函数时,要用预编译

81、命令 “#include”将有关的“头文件”包括到用户源 文件中.例如:在调用标准输入输出库函数时,文件开头应该有:#include “stdio.h” 或:#include头文件 4.4字符数据的输入输出(一).字符输出函数 一般形式:putchar(c)字符型变量 整型变量 函数作用:向终端输出一个字符 4.4字符数据的输入输出(续)例4.1 输出单个字符。运行结果:BO Y#include void main()char a,b,c; a=B;b=O;c=Y;putchar(a);putchar(b);putchar(c);putchar(n); putchar(a);putchar(n

82、);putchar(b);putchar(n);putchar(c);putchar(n);运行结果:BOY 4.4字符数据的输入输出(续)(二).字符输入函数 一般形式:getchar()函数作用:从终端(或系统隐含指定的输入设备)输入一个字符。函数值:从输入设备得到的字符。 4.4字符数据的输入输出(续)例4.2 输入单个字符。#include void main()char c; c=getchar(); putchar(c); putchar(n);运行程序:从键盘输入字符a按Enter键 屏幕上将显示输出的字符aaa 4.5格式输入与输出(一).格式输出函数函数作用:向终端(或系统隐

83、含指定的输出设备)输出若干个任意类型的数据。一般格式:printf(格式控制,输出表列)%d:以带符号的十进制形式输出整数%o:以八进制无符号形式输出整数%x:以十六进制无符号形式输出整数 Tobecontinued 4.5格式输入与输出(续)%u:以无符号十进制形式输出整数%c:以字符形式输出,只输出一个字符%s:输出字符串%f:以小数形式输出单,双精度数,隐含输出六位小数%e:以指数形式输出实数%g:选用%f或%e格式中输出宽度较短的一种格式,不输 出无意义的0 4.5格式输入与输出(续)几种常见的格式符的修饰符:L:用于长整型整数,可加在格式符d,o,x,u前面M(代表一个正整数):数据

84、最小宽度 N(代表一个正整数):对实数,表示输出n位小数;对字符串,表示截取的字符个数:输出的数字或字符在域内向左靠 4.5格式输入与输出(续)格式符。用来输出十进制整数。几种用法: :按十进制整型数据的实际长度输出。 :为指定的输出字段的宽度。如果数据的位数小于, 则左端补以空格,若大于,则按实际位数输出。例:(,);若,则输出结果为 , :输出长整型数据。例: ;/* 定义a为长整型变量*/ (,); 4.5格式输入与输出(续)(2) 格式符。以八进制整数形式输出。 输出的数值不带符号,符号位也一起作为八进制数的一部分输出。 例:int a=-1;printf(%d,%o,a,a);在内存

85、单元中的存放形式(以补码形式存放)如下: 1111111111111111输出为:,不会输出带负号的八进制整数。对长整数(型)可以 用“”格式输出。还可以指定字段宽度, 例:(,);输出为:177777。(数字前有2个空格) 4.5格式输入与输出(续)(3)格式符。以十六进制数形式输出整数。同样不会出 现负的十六进制数。例: ; (,);输出结果为:ffff,可以用“”输出长整型数,也可以指定输出字段的宽度例:“” 4.5格式输入与输出(续)(4)格式符,用来输出型数据. 一个有符号整数(型)也可以用格式输出; 一个型数据也可以用格式输出。 型数据也可用或格式输出。(5)格式符,用来输出一个字

86、符。如:d;(,d); 输出字符.一个整数,只要它的值在范围内,可以用“”使之按字符形式输出,在输出前,系统会将该整数 作为ASCII码转换成相应的字符;一个字符数据也可以用 整数形式输出。 4.5格式输入与输出(续)例4.3 无符号数据的输出。#include void main()unsigned int a=65535;int b=-2; printf(“a=%d,%o,%x,%un”,a,a,a,a);printf(“b=%d,%o,%x,%un”,b,b,b,b);运行结果:a=-1,177777,ffff,65535 b=-2,177776,fffe,65534 4.5格式输入与输

87、出(续)例4.4 字符数据的输出。#include void main()char c=a; int i=97;printf(“%c,%dn”,c,c);printf(“%c,%dn”,i,i);运行结果:指a定,9输7 出字数的宽度,ap,r9i7ntf(“%3c”,c);则输出:a 4.5格式输入与输出(续)(6)s格式符输出字符串. 。例如:(,) 输出字符串“”(不包括双引号)。 %ms,输出的字符串占m列,若串长大于m,则全部输出,若串长 小于m,则左补空格。 %-ms,若串长小于m,字符串向左靠,右补空格。 %m. ns,输出占m列,只取字符串中左端n个字符,输出在m列的 右侧,左

88、补空格。 %-m.ns,n个字符输出在m列的左侧,右补空格,若nm,m自动取n值。 4.5格式输入与输出(续)例4.5字符串的输出。#include void main()printf(“%3s,%7.2s,%.4s,%-5.3sn”, “CHINA”,“CHINA”, “CHINA”, “CHINA”);运行结果:CHINA,CH,CHIN,CHI 4.5格式输入与输出(续)(7)格式符。用来以小数形式输出实数(包括单双精度)有以下几种用法: 。不指定字段宽度,由系统自动指定字段宽度,使整数 部分全部输出,并输出位小数。应当注意,在输出的数字中 并非全部数字都是有效数字。单精度实数的有效位数

89、一般为位。.。指定输出的数据共占列,其中有位小数。如果 数值长度小于,则左端补空格。.与.基本相同,只是使输出的数值向左端 靠,右端补空格。 4.5格式输入与输出(续)例4.6输出实数时的有效位数。#include void () ,; (,);运行结果:. 4.5格式输入与输出(续)例4.7输出双精度数时的有效位数。#include void () ,; x=1111111111111.111111111;y=2222222222222.222222222;printf(“%f”,x+y);运行结果:3333333333333.333010 4.5格式输入与输出(续)例4.8 输出实数时指定

90、小数位数。#include void ()float f=123.456;printf(“%f%10f%10.2f%.2f%-10.2fn”,f,f,f,f,f);运行结果:123.455994123.455994123.46123.46123.46 4.5格式输入与输出(续)(8)格式符,以指数形式输出实数。可用以下形式: 。不指定输出数据所占的宽度和数字部分的小数位数. 例:(,); 输出: 6列5列所输出的实数共占列宽度。(注:不同系统的规定略有不同) 4.5格式输入与输出(续) m.ne和-m.ne。、和“”字符的含义与前相同。 此处指拟输出的数据的小数部分(又称尾数)的小数位数。 若

91、,则:rintf(%e%10e%10.2e%.2e%-10.2e,f,f,f,f,f);出如下:.234560e+0021.234560e+0021.23e+0021.23e+00213列13列10列9列1.23e+00210列输说明:未指定,自动使.超过给定的列,乃突破列的限制,按实际长度输出。第3个数据共占列,小数部分占列。只指定,未指定,自动使等于数据应占的长度。第5个数据应占列,数值只有列,由于是“数值向左靠,右补一个空格。(注:有的C系统的输出格式与此略有不同)p 输说明:1 未指定,自动使.超过给定的列,乃突破列的限制,按实际长度输出。 第3个数据共占列,小数部分占列。 只指定,未

92、指定,自动使等于数据应占的长度。 第5个数据应占列,数值只有列,由于是“” 数值向左靠,右补一个空格。 (注:有的C系统的输出格式与此略有不同) 4.5格式输入与输出(续)(9)格式符,用来输出实数.出如下:23.4680001.234680e+002123.46810列13列10列它根据数值的大小,自动选格式或格式(选择输出时占宽度 较小的一种),且不输出无意义的零。 例:若,则(,);输说明:1 用格式输出占列,用格式输出占列,用格式时,自动从上面两种格式中选择短者(今以格式为短) 故占列,并按格式用小数形式输出,最后 3个小数位为 无意义的,不输出,因此输出123.468,然后右补3个空

93、格。格式用得较少。 4.5格式输入与输出(续)说明:除了X,E,G外,其他各式字符必须用小写。可以在printf函数中的“格式控制”字符串中包含转 义字符。一个格式说明必须以“%”开头,以9个格式字符之 一为结束,中间可以插入附加格式字符。想输出%,则应该在格式控制字符串中用连续两 个%表示 4.5格式输入与输出(续)(一).格式输入函数函数作用:按照变量在内存的地址将变量值存 进去。一般格式:scanf(格式控制,地址表列)同printf函数是由若干个地址组成的表列,可以是变量的 地址,或字符串的首地址 4.5格式输入与输出(续)例4.9 用scanf函数输入数据。#includevoid

94、main()int a,b,c;a在内存中的地址&是地址运算符scanf(“%d%d%d”,&a,&b,&c);printf(“%d,%d,%dn”,a,b,c);运行情况:345(输入a,b,c的值)3,4,5(输出a,b,c的值) 4.5格式输入与输出(续)说明:对unsigned型变量所需要的数据,可以用%u,%d 或%o,%x格式输入。可以指定输入数据所占的列数,系统自动按它截 取所需数据。如果在%后有一个“*”附加说明符,表示跳过它 指定的列数。输入数据时不能规定精度。 4.5格式输入与输出(续)使用函数时应注意的问题:(1)函数中的“格式控制”后面应当是变量地址,而不应 是变量名。

95、(2) 如果在“格式控制”字符串中除了格式说明以外还有其他字符,则在输入数据时在对应位置应输入与这些字符相同的字符。 (3) 在用“”格式输入字符时,空格字符和“转义字符”都作为有效字符输入(4) 在输入数据时,遇以下情况时认为该数据结束。 遇空格,或按“回车”或“跳格”(Tab)键; 按指定的宽度结束,如“”,只取列; 遇非法输入。 4.6 顺序结构程序设计举例例4.10 输入三角形的三边 长,求三角形面积。假设:三个边长a,b,c能构 成三角形。已知面积公式:开始输入三边长 计算s 计算面积area=s(s a)(s b)(s c)结束s=(a+b+c)*0.5 4.6 顺序结构程序设计举

96、例(续)#include#include void main()float a,b,c,s,area;scanf(“%f,%f,%f,&a,&b,&c); s=1.0/2*(a+b+c);printf(“a=%7.2f,b=%7.2f,c=%7.2f,s=%7.2fn”,a,b,c,s); printf(“area=%7.2fn”,area);area=sqrt(s*(s-a)*(s-b)*(s-c);运行情况: 3,4,6数学函数库 因为要用到 其中的sqrt 函数a=3.00,b=4.00,c=6.00,s=6.50area=5.33 4.6 顺序结构程序设计举例(续)例4.11从键盘输入

97、一个大写字母,要求改用小写字母输出。 void() ,;();运行情况: ,(,); ; (,); 4.6 顺序结构程序设计举例(续)例4.12求x2方程的根。a,b,c由键盘输入,设b2 4ac。众所周知,一元二次方程式的根为x1= b +b2 4ac2ax2= b b 2 4ac2a可以将上面的分式分为两项:p= b2a, q= b 2 4ac2ax1=p+q,x2=p-q2fn,x1,x2);运行情况: , 4.6 顺序结构程序设计举例(续)#include #include void main ( )float a,b,c,disc,x1,x2,p,q;scanf(a=%f,b=%f,

98、c=%f,&a,&b,&c); disc=b*b-4*a*c;p=-b/(2*a);q=sqrt(disc)/(2*a); x1=p+q;x2=p-q; printf(nnx1=%5.2fnx2=%5.第五章本章要点关系表达式逻辑表达式选择结构程序设计主要内容5.1关系运算符和关系表达式5.2逻辑运算符和逻辑表达式5.3 if语句5.4 switch语句 5.5程序举例5.1 关系运算符和关系表达式(一).关系运算符及其优先次序 1. (小于)2. (大于)4. =(大于或等于)优先级相同(低)!=(不等于)5. =(等于)6. 说明:优先级相同(高)关系运算符的优先级低于算术运算符关系运算符

99、的优先级高于赋值运算符5.1 关系运算符和关系表达式(续)(二).关系表达式用关系运算符将两个表达式(可以是算术表达式或 关系表达式,逻辑表达式,赋值表达式,字符表达式)接起来的式子,称关系表达式C语言中没有专用 的逻辑值,1代表例:ab,a+bb+c,(a=3)(b=5),a真b),(0b代b”的值为“真”,表达式的值为1。5.2 逻辑运算符和逻辑表达式(一).逻辑运算符及其优先次序1. &(逻辑与)相当于其他语言中的AND2. |(逻辑或)相当于其他语言中的OR3. !(逻辑非)相当于其他语言中的NOT 例:a&b若a,b为真,则a&b为真。a|b若a,b之一为真,则a|b为真。!a若a为

100、真,则!a为假。 优先次序:!(非)-&()-|()逻辑运算符中的“&”和“|”低于关系运算符,“!”高于算 术运算符5.2 逻辑运算符和逻辑表达式(续)(二)逻辑表达式用逻辑运算符将关系表达式或逻辑量连接起来的式子就 是逻辑表达式逻辑表达式的值应该是一个逻辑量“真”或“假”。任何非零的数值被认作“真”例:设a=4,b=5:!a的值为0a&b的值为1a|b的值为1!a|b的值为1 4&0|2的值为15.2 逻辑运算符和逻辑表达式(续)53逻辑值为1!0逻辑值为14-1值为3表达式值为01&0逻辑值为083&8b)&(n=cd)当a=1,b=2,c=3,d=4,m和n的原值为1时,由于“ab”的

101、值 为0,因此m=0,而“n=cd”不被执行,因此n的值不是0而 仍保持原值1。5.2 逻辑运算符和逻辑表达式(续) 用逻辑表达式来表示闰年的条件能被4整除,但不能被100整除。能被4整除,又能被400整除答(year%4=0&year%100!=0)|year%400=0案值为真(1)是闰年,否则为非闰年。5.3 if语句一.If语句的三种基本形式表达式 真(非0)假(0)语句(1)if(表达式) 语句ch=getchar(); if(ch=a&ch=a&chy)printf(“%d”,x); elseprintf(“%d”,y);5.3 if语句(续)(3)if(表达式1)语句1 else

102、 if(表达式2)语句2 else if(表达式3)语句3else if(表达式m)语句m else语句n5.3 if语句(续)例:if (number500)cost=0.15;else if(number300)cost=0.10;else if(number100)cost=0.075; else if(number50)cost=0.05;elsecost=0;5.3 if语句(续)说明:(1).3种形式的if语句中在if后面都有表达式, 一般为逻辑表达式或关系表达式。(2).第二,第三种形式的if语句中,在每个else前面有一个分号,整个语句结束处有一 个分号。(3).在if和els

103、e后面可以只含有一个内嵌的操 作语句,也可以有多个操作语句,此时用花括号将几个语句括起来成为一个复合语句。5.3 if语句(续)例5.1 输入两个实数,按代数值由小到大的顺序输出这两 个数。#includevoid main()float a,b,t;yscanf(“%f,%f”,&a,&b);T=aA=b B=tif(ab)t=a; a=b; b=t;printf(“%5.2f,%5.2fn”,a,b);abn5.3 if语句(续)例5.2 输入三个数a,b,c,要求按由小到大的顺序输出。a和b交换a和c交换c和b交换abnyacnybcyIfab将a和b对换Ifac将a和c对换 Ifbc将

104、b和c对换5.3 if语句(续)#include void main ( ) float a,b,c,t; scanf(“%f,%f,%f”,&a,&b,&c);if(ab)t=a;a=b;b=t; if(ac)t=a;a=c;c=t; if(bc)t=b;b=c;c=t; printf(%5.2f,%5.2f,%5.2fn,a,b,c);5.3 if语句(续)二.If语句的嵌套 在if语句中又包含一个或多个if语句称为if语句的嵌套。 形式:If()Elseif()语句1 else语句2if()语句3 else语句4内嵌if5.3 if语句(续)例:例:If()If()if()语句1if()

105、语句1elseelseif()语句2if()语句2 else语句3else语句3匹配规则: Else总是与它上面的,最近的,同一复合语句中的,未配 对的if语句配对。当if和else数目不同时,可以加花括号来确定配对关系。5.3 if语句(续)-1(x0)算法1:算法1:输入x输入x若x0,则y=-1若x0,则y=1若x=0,则y=0输出y若x0,则y=1输出y5.3 if语句(续)#include void main()int x,y; scanf(“%d”,&x);程序段printf(“x=%d,y=%dn”,x,y);5.3 if语句(续)y=-1(x0)上例中的程序段有四个,请判断哪个

106、是正确的?程序1:程序2:正 确正 确if(x=0)Y=-1;if(x0)y=1;elseelsey=0;if(x=0)y=0;elsey=-1; elsey=1;程序3:程序4:y=-1;y=0;if(x!=0)if(x=0)If(x0)y=1;if(x0)y=1;elsey=0;elsey=-1;5.3 if语句(续)三.条件运算符格式:表达式?表达式表达式功能:判断表达式1的值,如果成立就执行表 达式2,否则就执行表达式3使用场合:若在语句中,当被判别的表达式的 值为“真”或“假”时,都执行一个赋值语句且向同一个变量赋值时,可以用一个 条件运算符来处理。5.3 if语句(续)例: ()

107、=; ;当ab时将a的值赋给max,当ab时将b的值赋给max, 可以看到无论ab是否满足,都是向同一个变量赋值。 可以用下面的条件运算符来处理:()?;5.3 if语句(续)说明:(1).条件运算符的执行顺序:先求解表达式,若为非(真) 则求解表达式,此时表达式的值就作为整个条件表达式 的值。若表达式的值为(假),则求解表达式,表达 式的值就是整个条件表达式的值。(2).条件运算符优先级高于赋值运算符,低于关系运算符 和算术运算符。(3).条件运算符的结合方向为“自右至左”。(4).“表达式2”和“表达式3”不仅可以是数值表达式,还可以 是赋值表达式或函数表达式。(5).条件表达式中,表达式

108、的类型可以与表达式和表达 式的类型不同。5.3 if语句(续)例5.4输入一个字符,判别它是否大写字母,如果是,将 它转换成小写字母;如果不是,不转换。然后输出最后 得到的字符。#include void main ( ) char ch;scanf(%c,& ch);如果字符变量ch的值为大写字母,则条件表达式的值为( ),即相应的小写字母。如果ch 的值不是大写字母,则条件表达式 的值为,即不进行转换。ch=(ch=A&ch=Z)?(ch+32):ch; printf(%cn,ch);5.4 switch语句switch语句的格式:(表达式)常量表达式:语句 常量表达式:语句常量表达式:语

109、句 :语句5.4 switch语句(续)例:要求按照考试成绩的等级输出百分制分数段,用 switch语句实现:()(); (); (); (); ();5.4 switch语句(续)说明:后面括弧内的“表达式”,标准允许 它为任何类型。(2) 当表达式的值与某一个后面的常量表达式的值相 等时,就执行此后面的语句,若所有的 中的常量表达式的值都没有与表达式的值匹配的,就执行 后面的语句。(3) 每一个的常量表达式的值必须互不相同,否则就 会出现互相矛盾的现象(对表达式的同一个值,有两种或 多种执行方案)。5.4 switch语句(续)(4)各个和的出现次序不影响执行结 果。例如,可以先出现“:”

110、,再出现 “:”,然后是“:”。(5)执行完一个后面的语句后,流程控制转移到下一 个继续执行。“常量表达式”只是起语句 标号作用,并不是在该处进行条件判断。在执行 语句时,根据后面表达式的值 找到匹配的入口标号,就从此标号开始执行下去,不再进 行判断。应该在执行一个分支后,可以用一个 语句来终止switch语句的执行。(6)多个可以共用一组执行语句。5.5 程序举例例55写程序,判断某一年是否闰年。5.5 程序举例(续)#include void main()int year, leap; scanf(%d,&year);if((year%4=0)!) ;if(yea(r%100=0)!)if

111、(y;ear%400=0)leap=1;elseleap=1; ;elseleap=0; ;if (leap) printf(%d is ,year);elseprintf(%d is not ,year); printf(a leap year.n); el(seleap=0;运行情况: !) . .5.5 程序举例(续)例5.7运输公司对用户计算运费。路程()越远,每公里运费越低。标准如下:没有折扣 折扣折扣折扣折扣折扣设每公里每吨货物的基本运费为,货物重为,距离为, 折扣为,则总运费的计算公式为: *()5.5 程序举例(续)分析折扣变化的规律性: 折扣的“变化点”都是的倍数 在横轴上加

112、一种坐标,c的值为s/250。c代表的倍数。,无折扣; ,折扣;,;,;,; ,。55555555.55555555 程序举例(续)程序举例(续)程序举例(续)程序举例(续)#include voidmain()intc,s;floatp,w,d,f; scanf(%f,%f,%d,&p,&w,&s); if(s=3000)c=12;elsec=s/250; switch(c)case0:d=0;break;case1:d=2;break;case2:case3:d=5;break;case4:case5:case6:case7:d=8;break;case8:case9:case10:cas

113、e11:d=10;break;case12:d=15;break;f=p*w*s*(1-d/100.0);printf(freight=%15.4fn,f);第六章本章要点循环的基本概念不同形式的循环控制多重循环问题主要内容6.1概述6.2goto语句以及用goto语句构成循 环6.3用while语句实现循环6.4用do-while语句实现循环 6.5用for语句实现循环6.6循环的嵌套6.7几种循环的比较6.8break语句continue和语句 6.9程序举例6.1 概述什么是循环?100问题1:y=nn=1为什么要使用循环?问题2:求学生平均成绩分数相加后除以课数在许多问题中需要用到循环

114、控制。循环结构 是结构化程序设计的基本结构之一,它和顺序 结构、选择结构共同作为各种复杂程序的基本 构造单元。6.2goto语句以及用goto语句构成循环1goto语句为无条件转向语句,它的一般形式 为goto语句标号; 语句标号用标识符表示,它的定名规则与变 量名相同,即由字母、数字和下划线组成, 其第一个字符必须为字母或下划线。例如:gotolabel_1;合法;goto123;不合法.6.2goto语句以及用goto语句构成循环结构化程序设计方法主张限制使用goto语句,因 为滥用goto语句将使程序流程无规律、可读性差. 一般来说,可以有两种用途:(1) 与if语句一起构成循环结构;(

115、2) 从循环体中跳转到循环体外。但是这种用法不符合结构化原则,一般不宜采 用,只有在不得已时(例如能大大提高效率)才使 用.100运行结果:5050 nn=1例6.1 用if语句和goto语句构成循环,求1到100的和void main( )int i, sum=0; i=1;loop:if(i=100) sum=sum+i; i+;说明:说明:说明:说明:这里用的是“当型”循环 结构,当满足“printf(%dni=100” 时执行花括弧内的循环体。goto loop;说明:这里用的是“当型”循环结构,当满足“i=100” 时 执行花括弧内的循环体。,sum);环结构。while语 先判断表

116、图6.3用while语句实现循环while语句用来实现“当型”循一般形式:while(表达式)语句当表达式为非0值时,执行 句中的内嵌语句。其特点是: 达式,后执行语句。图100 nn=1运行结果:5050例6.2 求1到100的和#include void main()int i,sum=0; i=1;while (i=100)说明:说明:说明:说明:(1)循环体如果包含一个以上 dn的语句,应该用花括弧括起来,以,sum);复合语句形式出现.(2)在循环体中 应有使循环趋向于结束的语句说明:(1)循环体如果包含一个以上 的语句,应该用花括弧括起来,以 复合语句形式出现.(2)在循环体中 应

117、有使循环趋向于结束的语句 sum=sum+i; i+;printf(%。6.3用while语句实现循环注意:循环体如果包含一个以上的语句,应该用 花括弧括起来,以复合语句形式出现。在循环体中应有使循环趋向于结束的语句。如果无此语句,则i的值始终不改变, 循环永不结束。循环,然后判断循环条件体语句,然后判 (“真”) 时,返 复,直到表达式。图6.4 用do-while语句实现do-while语句的特点:先执行循环体 是否成立。一般形式:do 循环体语句while(表达式);执行过程:先执行一次指定的循环 别表达式,当表达式的值为非零 回重新执行循环体语句,如此反 的值等于0为止,此时循环结束图

118、100 nn=1例6.3 求1到100的和运行结果:5050#include void main() int i,sum=0; i=1;dosum=sum+i; i+;while(i=100); printf(%dn,sum);6.4 用do-while语句实现循环while语句和用do-while语句的比较: 在一般情况下,用while语句和用do-while语 句处理同一问题时,若二者的循环体部分是一 样的,它们的结果也一样。但是如果while后面 的表达式一开始就为假(0值)时,两种循环的结 果是不同的。100 nn=11) #include st oid main ( )int sum

119、=0,iscanf(“%d while (i=sum=sum+I;+;rintf(“sum=%dinclude stdio.h main( )int sum=0,i;canf(”%d,&isum=sum+i; i+;hile (i(2) #运行结果:vvoid1 1 ;sum=55再运行一次:,&i);ssum=55);10)do再运行一次: 11i11n说明:说明:说明:说明:(1)当whilesum=11sum=11sum=11sum=11后面的表达式的第一次的值为“真”时,两种循printf(“sum=%dn”,sum); 环得到的结果相同。否则,二者结果不相同。wpsum=0,sum)

120、;说明:(1)当while后面的表达式的第一次的值为“真”时,两种循 环得到的结果相同。否则,二者 结果不相同。 6.5 用for 语句实现循环 C语言中的for语句使用最为灵活,不仅可以用于 循环次数已经确定的情况,而且可以用于循环次 数不确定而只给出循环结束条件的情况,它完全 可以代替while语句。一般形式:for(表达式1;表达式2;表达式3) 语句 6.5 用for 语句实现循环for语句的执行过程:(1) 先求解表达式1。(2) 求解表达式2,若其值为真(值为非0),则执 行for语句中指定的内嵌语句,然后执行下 面第(3)步。若为假(值为0),则结束循环, 转到第(5)步。(3)

121、 求解表达式3。(4) 转回上面第(2)步骤继续执行。(5) 循环结束,执行for语句下面的一个语句 6.5 用for 语句实现循环 执行表达式1循环初始条件表达式2?不成立循环控制条件成立执行语句for语句等价于下列语句:表达式1;while (表达式2)语句; 表达式3;循环体 执行表达式3 执行for循环之后的语句 6.5 用for 语句实现循环for语句最简单的应用形式也就是最易理解的如 下形式:for(循环变量赋初值;循环条件;循环变量增值)例如:for(i=1;i=100;i+) sum=sum+i; 它相当于以下语句:显然, 用for语句 简单、方便。i=1;while(i=10

122、0)sum=sum+i;i+; 6.5 用for 语句实现循环说明:说明:说明:说明:(1) for语句的一般形式中的“表达式1”可以省略,此 时应在for语句之前给循环变量赋初值。注意省略表 达式1时,其后的分号不能省略。如 forororor(;ii=100;100;100;100;iii+)ssssuuuummmm=ssssuuuummmm+ii;执行时,跳过“求解表达式1”这一步,其他不变。说明:(1) for语句的一般形式中的“表达式1”可以省略,此 时应在for语句之前给循环变量赋初值。注意省略表 达式1时,其后的分号不能省略。如for(;i=100;i+)sum=sum+i;执行

123、时,跳过“求解表达式1”这一步,其他不变。循环循环条件,循环无终 2始终为真。式2空缺。它相当于:图6.7 6.5 用for 语句实现说明:说明:说明:说明:(2) 如果表达式2省略,即不判断循环条件,循环无终止地进行下去。也就是认为表达式2始终为真。 例如:forororor(ii=1;1;1;1; ;ii+) ssssuuuummmm=ssssuuuummmm+ii; 表达式1是一个赋值表达式,表达式2空缺。它相当于:ii=1;1;1;1;wwwwhhhhiilllleeee(1)1)1)1)ssssuuuummmm=ssssuuuummmm+1;1;1;1;ii+;说明:(2) 如果表达

124、式2省略,即不判断止地进行下去。也就是认为表达式 例如:for(i=1; ;i+) sum=sum+i; 表达式1是一个赋值表达式,表达i=1;while(1)sum=sum+1;i+;图6.7 6.5 用for 语句实现循环说明:说明:说明:说明:(3) 表达式3也可以省略,但此时程序设计者应另外设 法保证循环能正常结束。如:forororor(ii=1;1;1;1;ii=100;100;100;100;)ssssuuuummmm=ssssuuuummmm+ii;ii+;在上面的for语句中只有表达式1和表达式2,而没有 表达式3。i+的操作不放在for语句的表达式3的位置 处,而作为循环体

125、的一部分,效果是一样的,都能使 循环正常结束。说明:(3) 表达式3也可以省略,但此时程序设计者应另外设 法保证循环能正常结束。如:for(i=1;i=100;)sum=sum+i;i+;在上面的for语句中只有表达式1和表达式2,而没有 表达式3。i+的操作不放在for语句的表达式3的位置 处,而作为循环体的一部分,效果是一样的,都能使 循环正常结束。 6.5 用for 语句实现循环说明:说明:说明:说明:(4) 可以省略表达式1和表达式3,只有表达式2,即只 给循环条件。如:fforororor(;ii=100;100;100;100;)wwwwhhhhiiilleeee(ii=100)1

126、00)100)100)ssssuuuummmm=ssssuuuummmm+iii;相当于ssssuuuummmm=ssssuuuummmm+ii;ii+;ii+;在这种情况下,完全等同于while语句。可见for语句 比while语句功能强,除了可以给出循环条件外,还可 以赋初值,使循环变量自动增值等。说明:(4) 可以省略表达式1和表达式3,只有表达式2,即只 给循环条件。如:for(;i=100;)while(i=100)sum=sum+i;相当于sum=sum+i;i+;i+;在这种情况下,完全等同于while语句。可见for语句 比while语句功能强,除了可以给出循环条件外,还可 以

127、赋初值,使循环变量自动增值等。说明:说明:说明:说明:(5) 3个表达式都可省略,如: for(; ;) 语句相当于 while(1) 语句即不设初值,不判断条件(认为表达式2为真值), 循环变量不增值。无终止地执行循环体。 6.5 用for 语句实现循环说明:(5) 3个表达式都可省略,如: for(; ;) 语句相当于 while(1) 语句即不设初值,不判断条件(认为表达式2为真值), 循环变量不增值。无终止地执行循环体。说明:说明:说明:说明:(6) 表达式1可以是设置循环变量初值的赋值表达式, 也可以是与循环变量无关的其他表达式。如:forororor (ssssuuuummmm=0

128、;0;0;0;ii=100;100;100;100;ii+)ssssuuuummmm=ssssuuuummmm+ii;表达式3也可以是与循环控制无关的任意表达式。 6.5 用for 语句实现循环说明:(6) 表达式1可以是设置循环变量初值的赋值表达式, 也可以是与循环变量无关的其他表达式。如:for (sum=0;i=100;i+)sum=sum+i;表达式3也可以是与循环控制无关的任意表达式。循环简单的表达式,也可 的简单表达式,中间ssssuuuummmm=ssssuuuummmm+iiii;-) kkkk=iiii+jjjj;达式,各包含两个赋 两个变量增值图6.8 6.5 用for 语

129、句实现说明:说明:说明:说明:表达式1和表达式3可以是一个简单的表达式,也可以 是逗号表达式,即包含一个以上的简单表达式,中间用逗号间隔。如:fforororor(ssssuuuummmm=0000,i=1;1;1;1;ii=100;100;100;100;iii+) ssssuuuummmm=ssssuuuummmm+ii;或fforororor(iii=0000,jjjj=100;100;100;100;iii=jjjj;ii+,jjjj-) kkkk=iii+jjjj; 表达式1和表达式3都是逗号表达式,各包含两个赋值表达式,即同时设两个初值,使两个变量增值.说明:表达式1和表达式3可以

130、是一个以是逗号表达式,即包含一个以上用 逗号间隔。如:for(sum=0,i=1;i=100;i+)或for(i=0,j=100;i=j;i+,j表达式1和表达式3都是逗号表值表达式,即同时设两个初值,使图6.8说明:说明:说明:说明:在逗号表达式内按自左至右顺序求解,整个逗号表达 式的值为其中最右边的表达式的值。如:fforororor(ii=1;1;1;1;ii=100;100;100;100;iii+,ii+) ssssuuuummmm=ssssuuuummmm+ii;相当于fforororor(ii=1;1;1;1;ii=100;100;100;100;ii=ii+2)2)2)2) s

131、sssuuuummmm=ssssuuuummmm+iii; 6.5 用for 语句实现循环说明:在逗号表达式内按自左至右顺序求解,整个逗号表达 式的值为其中最右边的表达式的值。如:for(i=1;i=100;i+,i+) sum=sum+i;相当于for(i=1;i=100;i=i+2) sum=sum+i;说明:说明:说明:说明:(7) 表达式一般是关系表达式(如i=100)或逻辑表达式 (如ab & xy),但也可以是数值表达式或字符表达 式,只要其值为非零,就执行循环体。 6.5 用for 语句实现循环说明:(7) 表达式一般是关系表达式(如i=100)或逻辑表达式 (如ab & xy)

132、,但也可以是数值表达式或字符表达 式,只要其值为非零,就执行循环体。赋给c,然后判断 换行符),如果,把本来要在循环 是一样的。可见 本来应在循环体图6.9 6.5 用for 语句实现循环说明:说明:说明:说明: forororor(iii=0;0;0;0;(cccc=gegegegettttcccchhhharararar()!=nnnn;ii+=cccc);在表达式2中先从终端接收一个字符赋给c,然后判断 此赋值表达式的值是否不等于n(换行符),如果 不等于n,就执行循环体。 注意:注意:注意:注意:此for语句的循环体为空语句,把本来要在循环 体内处理的内容放在表达式3中,作用是一样的。

133、可见 for语句功能强,可以在表达式中完成本来应在循环体 内完成的操作。说明: for(i=0;(c=getchar()!=n;i+=c);在表达式2中先从终端接收一个字符 此赋值表达式的值是否不等于n( 不等于n,就执行循环体。 注意:此for语句的循环体为空语句 体内处理的内容放在表达式3中,作用 for语句功能强,可以在表达式中完成 内完成的操作。图6.9 6.5 用for 语句实现循环说明: for(;(c=getchar()printf(%c for语句中只有表达式2其作用是每读入一个字符运行情况:说明:说明:说明:说明: for(;(c=getchar()!=n;) printf(

134、%c,c);for语句中只有表达式2,而无表达式1和表达式3。 其作用是每读入一个字符后立即输出该字符,直到输入 一个“换行”为止。请注意,从终端键盘向计算机输入 时,是在按Enter键以后才将一批数据一起送到内存缓 冲区中去的。)!=n;),c);,而无表达式1和表达式3。 后立即输出该字符,直到输Computer (输入) Computer (输出) 而不是Ccoommppuutteerr 入一个“换行”为止。请注意,从终端键盘向计算机输入时,是在按Enter键以后才将一批数据一起送到内存缓 冲区中去的。 6.5 用for 语句实现循环注意: C语言中的for语句比其他语言(如BASIC,

135、 PASCAL)中的FOR语句功能强得多。可以把循环体 和一些与循环控制无关的操作也作为表达式1或 表达式3出现,这样程序可以短小简洁。但过分 地利用这一特点会使for语句显得杂乱,可读性 降低,最好不要把与循环控制无关的内容放到 for语句中。6.6循环的嵌套一个循环体内又包含另一个完整的循环结构 称为循环的嵌套。内嵌的循环中还可以嵌套 循环,这就是多层循环。 三种循环(while循环、do-while循环和for循 环)可以互相嵌套。6.6循环的嵌套下面几种都是合法的形式:(1)while( )(2) do(3) for(;)while( )dofor(;) while( ); while

136、( );6.6循环的嵌套(4)while( )(5) for(;)(6) dodowhile( )for(;) while( )while( )6.7几种循环的比较(1)四种循环都可以用来处理同一问题,一般情 况下它们可以互相代替。但一般不提倡用 goto型循环。(2)在while循环和do-while循环中,只在while 后面的括号内指定循环条件,因此为了使循 环能正常结束,应在循环体中包含使循环趋 于结束的语句(如i+,或i=i+1等)。6.7几种循环的比较for循环可以在表达式3中包含使循环趋于 结束的操作,甚至可以将循环体中的操作全 部放到表达式3中。因此for语句的功能更强,凡用w

137、hile循环能完成的,用for循环都能实现。(3)用while和do-while循环时,循环变量初始 化的操作应在while和do-while语句之前完成。而for语句可以在表达式1中实现循环变量 的初始化。6.7几种循环的比较(4)while循环、do-while循环和for循环,可以 用break语句跳出循环,用continue语句结束本 次循环(break语句和continue语句见下节)。而 对用goto语句和if语句构成的循环,不能用 break语句和continue语句进行控制.6.8 break语句和continue语句6.8.1 break语句 break语句可以用来从循环体内

138、跳出循环体, 即提前结束循环,接着执行循环下面的语句一般形式:break;注意:break语句不能用于循环语句和switch语 句之外的任何其他语句中。6.8 break语句和continue语句例:float pi=3.14159;for(r=1;r100) break;printf(r=%f,area=%fn,r,area);程序的作用是计算r=1到r=10时的圆面积,直到 面积area大于100为止。从上面的for循环可以看 到:当area100时,执行break语句,提前结束 循环,即不再继续执行其余的几次循环。6.8 break语句和continue语句6.8.2 continue语

139、句 作用为结束本次循环,即跳过循环体中下面尚未执行的语句,接着进行下一次是否执行 循环的判定.一般形式:continue;语句语句语句语句语句语句语句语句别 环,而不是终止图6.8 break语句和continuecontinue语句和break语句的区 continue语句只结束本次循整个循环的执行。while(表达式1) for if(表达式2) continue;0图语句语句语句语句语句语句语句语句别 环过程,不再判图6.8 break语句和continuecontinue语句和break语句的区 break语句则是结束整个循断执行循环的条件是否成立。while(表达式1) for if

140、(表达式2) break;图100 nn=1例6.5 把100200之间的不能被3整除的数输出。#include void main()int n;for (n=100;n=200;n+)说明:说明:说明:说明:当n能被3整除时,执行continue rintf(%d语句,结束本次循环,n);(即跳过printf函数语句),只有n不能被3整除时才执行 printf函数。if (n%3=0)continue;p说明:当n能被3整除时,执行continue 语句,结束本次循环(即跳过printf函 数语句),只有n不能被3整除时才执行 printf函数。+1/5-1/7+公式求 的绝对值小于为止。

141、图6.9 程 序 举 例例6.6用/41-1/3的 近似值,直到某一项N-S结构化流程图表示算法图100 nn=1例6.6 求pi的近似值#include #include运行结果:pi=3.141594void main() int s;float n,t,pi; t=1;pi=0;n=1.0;s=1;while(fabs(t)1e-6)pi=pi+t;n=n+2;s=-s;t=s/n; pi=pi*4; printf(pi=%10.6fn,pi);F1前40个数。这个数列有如下,1。从第3个数开始,该 即:n=1) n=2)n3)图6.9 程 序 举 例例6.7求Fibonacci数列特点

142、:第1,2两个数为1数 是其前面两个数之和。F(1)=1(F(2)=1(F(n)=F(n-1)+F(n-2)(算法如图所示:图100 nn=1例6.7#incl求FFFFiiiibbbbononononacacacaccccciiii数列前40404040个数。 ude ain()long int f1,f2; nt i;1=1;f2=1;or(i=1; i=20; i+)printf(%12ld %12ld ,f1,f2); if(i%2=0) printf(n); f1=f1+f2;f2=f2+f1;运行结果:1123voidm581321345589144i233377610987f15

143、97258441816765f109461771128657463687502512139319641831781151422983204013462692178309352457857022887922746514930352241578173908816963245986102334155F1m能被2之中任何一个整,此时i必然小于或等于k( 之间的任一整数整除, i还要加1,因此i=k+1,之后判别i的值是否大于或 曾被2k之间任一整数整图6.9 程 序 举 例 例6.8 判断m是否素数。 算法思想:让m被2到除,如果数整除,则提前结束循环即);如果m不能被2k(即) 则在完成最后一次循环

144、后, 然后才终止循环。在循环 等于k+1,若是,则表明未 除过,因此输出“是素数”。如图所示:图100 nn=1运行结果:1717 is a prime number例6.8判断m是否素数。#include #include void main()int m,i,k; scanf(%d,&m);k=sqrt(m); for (i=2;ik) printf(%d is a prime numbern,m); else printf(%d is not a prime numbern,m);100 nn=1的全部素数。例6.9 求100200间#include stdio.h# include m

145、ath.h void main()运行结果:101 103 107 109 113 127131 137 139 149 151 157 163 167 173179 181 191 193 197 199int m,k,i,n=0; for(m=101;m=200;m=m+2) k=sqrt(m);for (i=2;i=k+1)printf(%d ,m);n=n+1;if(n%10=0) printf(n);printf (n);F1保密,往往按一定规律将 按约定的规律将其译回原文变成密码: e,即变成其后的第4个字 成C,Z变成D。素数。图图6.9 程 序 举 例例6.10 译密码。为使电

146、文 其转换成密码,收报人再 文。 例如:可以按以下规律将电 将字母A变成字母E,a变成 母,W变成A,X变成B,Y变如图所示100 nn=1出其相应的密码例6.10输入一行字符,要求输 include void main()char c;运行结果:China! Glmre!while(c=getchar()!=n)if(c=a & c=A & cZ & cz) c=c-26;printf(%cn,c);第七章第七章数组问题:给一组数排序,这组数该如何存放1111111111111188888888888888882945631 8些88878111111116 88 8据如何存8放? 这88数

147、8 8 8 8 8888才便于排序这便是本章所要解决的问题第七章数组本章要点掌握一维、二维数组的定义和引用方法、存储结构和初始化方法。掌握有关一维数组的有关算法。掌握数组的运算。第七章数组主要内容7.1一维数组的定义和引用7.2二维数组的定义和引用7.3字符数组7-1 一维数组的定义和引用 问题:有如下几组数据,它们分别该如何存储呢?一个班学生的学习成绩一行文字一个矩阵一个班学生的学习成绩一行文字一个矩阵这些数据的特点是:1、具有相同的数据类型数组是一组具有相同数据类型的数据的有序集合。2、使用过程中需要保留原始数据 C语言为这些数据,提供了一种 构造数据类型:数组。 7.1.1一维数组的定义

148、1、一维数组的定义格式为: 类型说明符 数组名常量表达式; 例如:inta10;它表示定义了一个整形数组,数组名为a,此数组 有10个元素。2、说明:(1)数组名定名规则和变量名相同,遵循标识符定 名规则。(2)在定义数组时,需要指定数组中元素的个数,方括弧中的常量表达式用来表示元素的个数,即数 组长度。例如,指定a10,表示a数组有10个元 素,注意下标是从0开始的,这10个元素是,a0,a1,a2,a3,a4,a5,a 6,a7,a8,a9。请持别注意,按上 面的定义,不存在数组元素a10。(3)常量表达式中可以包括常量和符号常量,但不 能包含变量。也就是说,C语言不允许对数组的大小 作动

149、态定义,即数组的大小不依赖于程序运行过程 中变量的值。例如,下面这样定义数组是不行的:数组说明中其他常见的错误举例:intn;scanf(“%d,&n);/*在程序中临时输入数组的大小*/intan;floata0; intb(2)(3);/*/*数组大小为0没有意义*/ 不能使用圆括号*/intk,ak;/*不能用变量说明数组大小*/3、一维数组在内存中的存放一维数组:floatmark100;每个数据元素占用的 字节数,就是基类型 的字节数 一个元素占4个字节低地址高地址mark0 mark1 mark2 mark386.592.077.552.0.94.0.mark997.1.2一维数组

150、元素的引用1、数组元素的引用方式:数组名下标 下标可以是整型常量或整型表达式。例如: a0=a5+a7-a2*3定义数组时用到的“数组名常量表达式” 和引用数组元素时用到的“数组名下标” 是有区别的。例如 int a10;t=a6;/* 定义数组长度为10 */* 引用a数组中序号为6的元 素。此时6不代表数组长度 */注意2、一维数组元素引用的程序实例#include void main()int i,a10;for (i=0; i=0; i-) printf(%d ,ai); printf(n);#include void main()运行结果如下:9 8 7 6 5 4 3 2 1 0程

151、序使a0到 a9的值为09, 然后按逆序输出。int i,a10;for (i=0; i=0; i-) printf(%d ,ai); printf(n);7.1.3一维数组的初始化1、对数组元素初始化的实现方法:(1)在定义数组时对数组元素赋以初值。例如:inta10=0,1,2,3,4,5,6,7,8,9; 将数组元素的初值依次放在一对花括弧内。经过上面 的定义和初始化之后,a0=0,a1=1,a2=2,a3=3,a4=4,a5=5,a6=6,a7=7,a8=8,a9=9。2)可以只给一部分元素赋值。例如:inta10=0,1,2,3,4;定义a数组有10个元素,但花括弧内只提供5个初值,

152、 这表示只给前面5个元素赋初值,后5个元素值为0。(3)如果想使一个数组中全部元素值为0,可以写成inta10=0,0,0,0,0,0,0,0,0,0;或inta10=0; 不能写成:inta10=0*10;这是与FORTRAN语言不同的,不能给数组整体赋初值。4)在对全部数组元素赋初值时,由于数据的个数已经确定,因此可以不指定数组长度。例如:inta5=1,2,3,4,5; 也可以写成int a=1,2,3,4,5;在第二种写法中,花括弧中有5个数,系统就会据此 自动定义a数组的长度为5。但若数组长度与提供初值 的个数不相同,则数组长度不能省略。例如,想定义 数组长度为10,就不能省略数组长

153、度的定义,而必须 写成int a10=1,2,3,4,5;只初始化 前5个元素,后5个元素为0。7.1.4一维数组程序举例程序举例1:用数组来处理,求解Fibonacci数列。Fibonacci数列公式:已知:a1=a2=1 an=an-1+an-2程序实例:程序实例:程序实例:程序实例:#include voidmain()inti;intf20=1,1;即:1,1,2,3,5,8,13程序实例:#include voidmain()inti;intf20=1,1;for(i=2;i20;i+) fi=fi-2+fi-1; for(i=0;i20;i+)if(i%5=0)printf(n);

154、printf(%12d,fi)/*For循环结束*/*程序结束*/for(i=2;i20;i+)fi=fi-2+fi-1; for(i=0;i20;i+) if(i%5=0) printf( n);printf(%12d,fi)/*For循环结束*/*程序结束*/11235813213455891442333776109871597258441816765运行结果如下:if语句用来控 制换行,每行 输出5个数据。程序举例2:用起泡法对10个数排序(由小到大)。起泡法的思路是:将相邻两个数比较,将小的调到前头。第 一 趟 比 较注经过第一趟(共5次比较与交换)后,最大的数9已“沉 底”。然后进行

155、对余下的前面5个数第二趟比较,经过第二趟(共4次比较与交换)后,得到次大的数8。第 二 趟 比 较注如果有n个数,则要进行n-1趟比较。在第1趟比较中 要进行n-1次两两比较,在第j趟比较中要进行n-j次两 两比较。程序流程图如下:程序实例程序实例程序实例程序实例7.37.37.37.3:#include void main()int a10; int i,j,t;printf(input 10 numbers :n); for (i=0;i10;i+) scanf(%d,&ai);printf(n);程序实例7.3:#include void main()int a10; int i,j,t

156、;printf(input 10 numbers :n); for (i=0;i10;i+) scanf(%d,&ai);printf(n);for(j=0;j9;j+)for(i=0;iai+1)t=ai;ai=ai+1;ai+1=t;he sorted numbers :n);10;i+)%d ,ai); n);程序运行结果如下:printf(tinput10numbers:for(i=0;i1 0 4 8 12 65 -76 100 -45 123for(j=0;j9;j+) for(i=0;iai+1)t=ai;ai=ai+1; ai+1=t;printf(the sorted num

157、bers :n); for(i=0;i例如:a=12314456b=2536例7.4将一个二维数组行和列元素互换,存到另一个 二维数组中。#include void main()int a23=1,2,3,4,5,6; int b32,i,j;printf(array a:n); for (i=0;i=1;i+)for (j=0;j=2;j+)#include stdio void main()int a23=1,2,3,4,5,6; int b32,i,j;printf(array a:n);for (i=0;i=1;i+)for (j=0;j=2;j+)printf(%5d,aij); b

158、ji=aij;printf(n);printf(array b:n); for (i=0;i=2;i+)for(j=0;j=1;j+) printf(%5d,bij); printf(n); /*程序结束*/printf(%5d,aij); bji=aij;运行结果如下:printf(n);printf(array b:n); for (i=0;i=2;i+)for(j=0;j=1;j+)arrayarraya:123456b:1425printf(%5d,bij)36 printf(n); /*程序结束*/7.2.4二维数组程序举例例7.5:有一个34的矩阵,要求编程序求出其中值最大 的那个

159、元素的值,以及其所在的行号和列号。先用N-S流程图表示算法 ,如下:程序如下:#include void main()int i,j,row=0,colum=0,max;int a 3 4 =1 , 2 , 3 , 4,9 , 8 , 7 , 6,-10,10,-5,2;max=a00;程序如下:#include void main()int i,j,row=0,colum=0,max;int a 3 4 =1 , 2 , 3 , 4,9 , 8 , 7 , 6,-10,10,-5,2;max=a00;for (i=0;i=2;i+)for (j=0;jmax)max=aij; row=i;c

160、olum=j;printf(max=%d,row=%d,colum=%dn,max,row,colum); /*程序结束*/for (i=0;i=2;i+)for (j=0;jmax)max=aij; row=i;colum=j;n,printf(max=%d,row=%d,colum=%dmax,row,colum); /*程序结束*/的数组是字符数组。字符数组中的一个 元素存放一个字符。定义方法与前面介绍的类似。例如:charc10;c0=I;c1=;c2=a; c3=m;c4=;c5=h;c6=a; c7=p;c8=p;c9=y;7-3 字符数组7.3.1字符数组的定义用来存放字符数据7

161、.3.2字符数组的初始化对字符数组初始化,最容易理解的方式是逐个字符 赋给数组中各元素。如:char c10= I, ,a,m,,h,a,p,p,y;如果在定义字符数组时不进行初始化,则数组中各元 素的值是不可预料的。如果花括弧中提供的初值个数(即字符个数)大于数组 长度,则按语法错误处理。如果初值个数小于数组长度,则只将这些字符赋给数 组中前面那些元素,其余的元素自动定为空字符 (即0)。例如:char c10=c, ,p,r,o,g,r,a,m;如果提供的初值个数与预定的数组长度相同,在定义 时可以省略数组长度,系统会自动根据初值个数确定 数组长度。例如:char c=I, ,a,m, ,

162、h,a,p,p,y;数组c的长度自动定为10。也可以定义和初始化一个二维字符数组。例如:chardiamond55=,*,*,*,*,*,*,*,*程序如下:#include voidmain()charc10=I, ,a,m, ,a, b,o,y;inti; for(i=0;i10;i+)printf(%c,ci); printf(n); 7.3.3字符数组的引用例7.6 输出一个字符串。运行结果:Iamaboy程序如下:#include voidmain()charc10=I, ,a,m, ,a, ,b,o,y;inti; for(i=0;i10;i+)printf(%c,ci); pri

163、ntf(n); 7.3.3字符数组的引用例7.7 输出一个钻石图形#include voidmain()chardiamond5=,*,*,*,*, , , * , , * , ,*,*; inti,j;运行结果* * *for(i=0;i5;i+)for(j=0;j5;j+) printf(%c,diamondij); printf(n); 7.3.4字符串和字符串结束标志为了测定字符串的实际长度,C语言规定了一个“字符 串结束标志”,以字符0作为标志。如果有一个 字符串,前面9个字符都不是空字符(即0) , 而第10个字符是0,则此字符串的有效字符为9 个。系统对字符串常量也自动加一个0作

164、为结 束符。0代表ASCII码为0的字符,从ASCII码表中可以 查到,ASCII码为0的字符不是一个可以显示的字符, 而是一个“空操作符”,即它什么也不干。用它来作为 字符串结束标志不会产生附加的操作或增加有效字 符,只起一个供辨别的标志。 7.3.4字符串和字符串结束标志可以用字符串常量来使字符数组初始化。注意例如char c=Iamhappy;也可以省略花括弧,直接写成 char c=“I am happy;它与下面的数组初始化等价char c = I, , a, m,h,a,p,p,y,0再比如 charc10=China;数组c的前5个元素为C,h,i,n,a,第6个元素为0,后4个

165、元素也设定为空字 符。需要说明的是:字符数组并不要求它的最后一个字符为0,甚至可以不包含0。例如:charc5=C,h,i,n,a;这样写完全是合法的。但是由于系统对字符串常量自动加一个0。因此,人们为了使处理方法 一致,在字符数组中也常人为地加上一个0。如: charc6=C,h,i,n,a,0;这样做,这样做是为了便于引用字符数组中 的字符串。例如定义了以下的字符数组char c=”Pascal program”;现在,想用一个新的字符串代替原有的字符串”Pascal program” ,从键盘向字符数组输入Hello 如果不加0的话,字符数组中的字符如下Hellol program7-3

166、 字符数组7.3.5字符数组的输入输出字符数组的输入输出可以有两种方法:逐个字符输入输出。用格式符“%c”输入或输出一个字符。 将整个字符串一次输入或输出。用“%s”格式符, 意思是对字符串的输入输出。在内存中数组c的状态例如charc=China; printf(%s,c);说明 用“%s”格式符输出字符串时,printf函数中的输出 项是字符数组名,而不是数组元素名。如:写成下 面这样是不对的:printf(%s,c0); 如果数组长度大于字符串实际长度,也只输出到遇0结束。 输出字符不包括结束符0。如:char c10=China;/* 字符串 长度为5,连0共占6个字节 */print

167、f(%s,c);只输出字符串的有效字符 “China”,而不是输出10个字符。这就是用字符串 结束标志的好处。说明(4)如果一个字符数组中包含一个以上0,则遇 第一个0时输出就结束。 可以用scanf函数输入一个字符串。 例如scanf(%s,c);scanf函数中的输入项c是已定义的字符数组名,输 入的字符串应短于已定义的字符数组的长度。例 如,定义char c6;从键盘输入:China系统自动在China后面加一个0结束 符。如果利用一个scanf函数输入多个字符串,则在输入时以空格分隔。例如:charstrl5,str25,str35; scanf(%s%s%s,str1,str2,st

168、r3); 输入数据:How are you? 数组中未被赋值的元素的值自动置0。若改为charstr13;scanf(%s,str); 如果输入以下12个字符Howareyou?大家思考一下str13数组中存放的结果会是什么呢?由于系统把空格字符作为输入的字符串之间的分隔符, 因此只将空格前的字符“How”送到str中。由于把“How”作为一个字符串处理,故在其后加0。需要注意:scanf函数中的输入项如果字符数组名。不要再加地址符&,因为在C语言中数组名代表该数 组的起始地址。下面写法不对: scanf(%s,&str);分析图中所示的字符数组用8进制形式输出数组c的起始地址 printf(

169、%o,c);输出数组c的起始地址2000。 printf(%s,c);按字符数组名c找到其数组起始 地址,然后逐个输出其中的字符,直到遇0为 止。在C的函数库中提供了一些用来处理字符串的函数, 用方便。几乎所有版本的C 编译系统都提供这些函数 下面介绍几种常用的函数 7.3.6字符串处理函数1. puts函数其一般形式为:puts(字符数组)使其作用是将一个字符串(以0结束的字 。符序列)输出到终端。假如已定义str是一个字。符数组名,且该数组已被初始化为China。 则执行puts(str);其结果是在终端上输出 China。由 于 可 以 用 printf 函 数 输 出 字 符 串 ,

170、因 此 puts函数用的不多。用puts函数输出的字符串中可以包含转义字符。例如:char str=ChinanBeijing; puts(str);在输出时,将字符串 结束标志0 转换成n, 即输出完字符串后换行。输出结果:China Beijing 7.3.6字符串处理函数2. gets函数其一般形式为:gets(字符数组) 其作用是从终端输入一个字符串到字符数 组,并且得到一个函数值。该函数值是字符 数组的起始地址。如执行下面的函数: gets(str)从键盘输入: Computer将输入的字符串Computer送给字符数组str(请注意送给数组的共有9个字符,而不是8个 字符),函数值

171、为字符数组str的起始地址。 一般利用gets函数的目的是向字符数组输入一 个字符串,而不大关心其函数值。注意:用puts和gets函数只能输入或输出一个 字符串,不能写成puts(str1,str2)或gets(str1,str2) 7.3.6字符串处理函数3. strcat函数其一般形式为:strcat(字符数组1,字符数组2) Strcat的作用是连接两个字符数组中的字符串, 把字符串2接到字符串1的后面,结果放在字符数组1中,函数调用后得到一个函数值字符数组1的地址。例如:char str130=PeoplesRepublicof;charstr2=China; print(%s,st

172、rcat(str1,str2); 输出:PeoplesRepublicofChina 7.3.6字符串处理函数4. strcpy函数 其一般形式为:strcpy(字符数组1,字符串2) strcpy是“字符串复制函数”。作用是将字符 串2复制到字符数组1中去。例如:charstr110,str2=China;strcpy(str1,str2);1.字符数组1必须定义得足够大,以便容纳被复制的 字符串。字符数组1的长度不应小于字符串2的长度。2.“字符数组1”必须写成数组名形式(如str1),“字符串2”可以是字符数组名,也可以是一个字符串常量。如strcpy(str1,China);3.复制时

173、连同字符串后面的0一起复制到字符 数组1中。4.可以用strcpy函数将字符串2中前面若干个字符复 制到字符数组1中去。例如:strcpy(str1,str2,2);作用是将str2中前面2个字符复制到str1中去,然后 再加一个0。5.不能用赋值语句将一个字符串常量或字符数组直接给一个字符数组。如下面两行都是不合法的: str1=China;str1=str2;而只能用strcpy函数将一个字符串复制到另一个字符 数组中去。用赋值语句只能将一个字符赋给一 个字符型变量或字符数组元素。如下面是合法的:chara5,c1,c2; c1=A;c2=B;a0=C;a1=h;a2=i; a3=n;a4

174、=a; 7.3.6字符串处理函数5. strcmp函数其一般形式为:strcmp(字符串1,字符串2)strcmp的作用是比较字符串1和字符串2。 例如:strcmp(str1,str2);strcmp(China,Korea); strcmp(str1,Beijing);字符串比较的规则与其他语言中的规则相同,即对两 个字符串自左至右逐个字符相比(按ASCII码值大小比 较),直到出现不同的字符或遇到0为止。如全 部字符相同,则认为相等;若出现不相同的字符,则 以第一个不相同的字符的比较结果为准。例如 AA,computercompare, 36+54!$&#, CHINACANADA,DO

175、G字符串2,函数值为一正整数。(3)如果字符串1str2)printf(yes);而只能用if(strcmp(str1,str2)0)printf(yes); 7.3.6字符串处理函数6. strlen函数 其一般形式为:strlen (字符数组)strlen是测试字符串长度的函数。函数的值为字 符串中的实际长度(不包括0在内)。 如:charstr10=China;printf(%d,strlen(str);输出结果不是10,也不是6,而是5。也可以直接 测试字符串常量的长度,如strlen(China); 7.3.6字符串处理函数7. strlwr函数其一般形式为:strlwr (字符串)

176、strlwr函数的作用是将字符串中大写字母换成小写字母。8. strupr函数其一般形式为:strupr (字符串)strupr函数的作用是将字符串中小写字母换成大写字母。以上介绍了常用的8种字符串处理函数,应当再次强 调:库函数并非C语言本身的组成部分,而是C编译 系统为方便用户使用而提供的公共函数。不同的编 译系统提供的函数数量和函数名、函数功能都不尽 相同,使用时要小心,必要时查一下库函数手册。 7.3.7字符数组应用举例例7.8输入一行字符,统计其中有多少个单词,单词之间用空格分隔开。程序如下:#include void main()char string81; int i,num=0

177、,word=0; char c;gets(string);for (i=0;(c=stringi)!=0;i+)程序如下:#include void main()char string81; int i,num=0,word=0; char c;gets(string);for (i=0;(c=stringi)!=0;i+)if(c= ) word=0;else if(word=0)word=1; num+;printf(There are %d words in the line.n,num);if(c= ) word=0;else if(word=0)word=1; num+;运行情况如下

178、: I am a boy.There are 4 words in the line.printf(There are %d words in the line.n,num);程序如下:#include#include void main ( )char string20; char str320; int i;for (i=0;i3;i+) gets (stri);例7.9有3个字符串,要求找出其中最大者程序如下:#include#include void main ( )char string20; char str320; int i;for (i=0;i0) strcpy(string

179、,str0)else strcpy(string,str1); if (strcmp(str2,string)0)strcpy(string,str2); printf(nthe largest string isn%sn,string);if (strcmp(str0,str1)0) strcpy(string,str0)else strcpy(string,str1); if (strcmp(str2,string)0)strcpy(string,str2); printf(nthe largest string isn%sn,string);运行结果如下: CHINA HOLLAND A

180、MERICAthe largest string is HOLLAND第八章本章要点函数的概念函数的定义与调用函数的递归调用变量的作用域函数的作用域主要内容8.1概述8.函数定义的一般形式8.函数参数和函数的值8.函数的调用8.函数的嵌套调用8.函数的递归调用8.数组作为函数参数8.8局部变量和全局变量8.变量的存储类别8.10内部函数和外部函数由主函数调用其他函数,其他函数也可以互相调用。 同一个函数可以被一个或多个函数调用任意多次。函数间的调用关系一个较大的程序可分为 若干个程序模块,每一 个模块用来实现一个特定的功能。在高级语言 中用子程序实现模块的 功能。子程序由函数来完成。一个程序可

181、由 一个主函数和若干个其 他函数构成。8.1概述# iinnnnccccllluuuudddeeee voivoivoidddd mmmmainnnn()voivoivoidddd pppprrrriinnnnttttssssttttarararar();/*对pppprrrriinnnnttttssssttttarararar函数声明*/ voivoivoidddd pppprrrriinnnntttt_m_m_m_meeeessssssssageageageage();/*对pppprrrriinnnntttt_m_m_m_meeeessssssssageageageage函数声明*/pp

182、pprrrriinnnnttttssssttttarararar();*调用pppprrrriinnnnttttssssttttarararar函数* pppprrrriinnnntttt_m_m_m_meeeessssssssageageageage();/*调用pppprrrriinnnntttt_m_m_m_meeeessssssssageageageage函数*/ pppprrrriinnnnttttssssttttarararar();*调用pppprrrriiinnnnttttssssttttarararar函数*/例8.1先举一个函数调用的简单例子# include void m

183、ain()void printstar();/*对printstar函数声明*/void print_message();/*对print_message函数声明*/printstar();*调用printstar函数* print_message();/*调用print_message函数*/ printstar();*调用printstar函数*/voivoidddd pppprrrriinnnnttttssssttttarararar() *定义pppprrrriinnnnttttssssttttarararar函数*pppprrrriiinnnnttttf(* * * * * * *

184、* * * * * * * * *nnnn);voivoidddd pppprrrriiinnnntttt_m_m_m_meeeessssssssageageageage() *定义pppprrrriinnnntttt_m_m_m_meeeessssssssageageageage函数*pppprrrriiinnnnttttf(Howowowow ddddoooo youyouyouyou ddddo!nnnn);void printstar() *定义printstar函数*printf(* * * * * * * * * * * * * * * *n);void print_message

185、() *定义print_message函数*printf(How do you do!n);运行情况如下:* Howdoyoudo!*说明:(1)一个程序由一个或多个程序模块组成, 每一个程序模块作为一个源程序文件。对较 大的程序,一般不希望把所有内容全放在一 个文件中,而是将他们分别放在若干个源文 件中,再由若干源程序文件组成一个C程序。 这样便于分别编写、分别编译,提高调试效 率。一个源程序文件可以为多个C程序公用。(2) 一个源程序文件由一个或多个函数以及其他有关内容(如命令行、数据定义等)组成。一个源程序文件是一个编译单位,在程序编 译时是以源程序文件为单位进行编译的,而不 是以函数为

186、单位进行编译的。(3)程序的执行是从函数开始的,如是在函数中调用其他函数,在调 用后流程返回到函数,在函 数中结束整个程序的运行。(4) 所有函数都是平行的,即在定义函数时是分别进行的,是互相独立的。一个函数并不 从属于另一函数,即函数不能嵌套定义。函数 间可以互相调用,但不能调用函数。 函数是系统调用的。(5)从用户使用的角度看,函数有两种: 标准函数,即库函数。这是由系统提供的,用户不必自己定义这些函数,可以直接使 用它们。应该说明,不同的C系统提供的库函 数的数量和功能会有一些不同,当然许多基 本的函数是共同的。 用户自己定义的函数。用以解决用户的专 门需要。(6) 从函数的形式看,函数

187、分两类: 无 参 函 数 。 如 例 8.1 中 的 printstar 和 print_message就是无参函数。在调用无参函 数时,主调函数不向被调用函数传递数据。无 参函数一般用来执行指定的一组操作。例如, 例8程序中的printstar函数。有参函数。在调用函数时,主调函数在调用 被调用函数时,通过参数向被调用函数传递数 据,一般情况下,执行被调用函数时会得到一 个函数值,供主调函数使用。8.函数定义的一般形式定义无参函数的一般形式为:类型标识符函数名()声明部分 语句部分在定义函数时要 用“类型标识符” 指定函数值的类 型,即函数带回 来的值的类型。 例8.中的 printstar

188、和 print_message函 数为void类型, 表示不需要带回 函数值。8.2.1. 无参函数的定义一般形式定义无参函数的一般形式为:类型标识符声明部分 语句部分函数名()8.2.2. 有参函数定义的一般形式定义有参函数的一般形式为:类型标识符函数名(形式参数表列)声明部分 语句部分定义有参函数的一般形式为:类型标识符函数名(形式参数表列)声明部分 语句部分例如: (int ,int ) ;/ *函数体中的声明部分* ?; ();8.2.3 空函数定义空函数的一般形式为:类型标识符函数名() 例如:() 定义空函数的一般形式为: 类型标识符函数名() 例如:() 调用此函数时,什么 工作

189、也不做,没有任 何实际作用。在主调 函数中写上“ ();”表明“这 里要调用一个函 数”,而现在这个函 数没有起作用,等以 后扩充函数功能时补 充上。8.函数参数和函数的值8.形式参数和实际参数在前面提到的有参函数中,在定义函数时函数名后面括弧中的变量名称为“形式参数”( 简称“形参”),在主调函数中调用一个函数时,函数名后面括弧中的参数(可以是一个表 达式)称为“实际参数”(简称“实参”)。 return后面的括弧中的值()作为函数带回的 值(称函数返回值)。大多数情况下,主调函数和被调用函数之间有 数据传递的关系。在不同的函数之间传递数据,可以使用的方法: 参数:通过形式参数和实际参数 返

190、回值:用return语句返回计算结果 全局变量:外部变量例8.调用函数时的数据传递#include void()intmax(int,int);/*对函数的声明*/ int,;scanf(,); (,); printf(,);#include void()intmax(int,int);/*对函数的声明*/ int,;scanf(,); (,); printf(,);intmax(int,int)*定义有参函数max*int; ?; return();intmax(int,int)*定义有参函数max*int; ?; return();运行情况如下: , 通过函数调用,使两个函数中的数据发生联

191、系关于形参与实参的说明:(1) 在定义函数中指定的形参,在未出现函数 调用时,它们并不占内存中的存储单元。只 有在发生函数调用时,函数中的形参 才被分配内存单元。在调用结束后,形参所 占的内存单元也被释放。(2) 实参可以是常量、变量或表达式,如: (,); 但要求它们有确定的值。在调用时将实参的值赋给形参。例如实参值a为3.5,而形参x为整型,则将实 数3.5转换成整数3,然后送到形参b。字符型 与整型可以互相通用。(3) 在被定义的函数中,必须指定形参的类 型(见例8.2程序中的 “(, );” )。(4) 实参与形参的类型应相同或赋值兼容。 例8中实参和形参都是整型。如果实参为 整型而形

192、参x为实型,或者相反,则按第3章 介绍的不同类型数值的赋值规则进行转换。例如实参值a为3.5,而形参x为整型,则将实 数3.5转换成整数3,然后送到形参b。字符型 与整型可以互相通用。(5) 在语言中,实参向对形参的数据传递 是“值传递”,单向传递,只由实参传给形 参,而不能由形参传回来给实参。在内存 中,实参单元与形参单元是不同的单元。在调用函数时,给形参分配存储单元,并将实 参对应的值传递给形参,调用结束后,形参单 元被释放,实参单元仍保留并维持原值。因 此,在执行一个被调用函数时,形参的值如果 发生改变,并不会改变主调函数的实参的值。 例如,若在执行函数过程中和的值变为和,而和仍为和。8

193、.3.2函数的返回值通常,希望通过函数调用使主调函数能得到一个确 定的值,这就是函数的返回值。例如,例8.中, (,)的值是,(,)的 值是5。赋值语句将这个函数值赋给变量。通常,希望通过函数调用使主调函数能得到一个确 定的值,这就是函数的返回值。例如,例8.中, (,)的值是,(,)的值是5。赋值语句将这个函数值赋给变量。关于函数返回值的一些说明:(1)函数的返回值是通过函数中的return语句获 得的。如果需要从被调用函数带回一个函数值供主调函数使 用,被调用函数中必须包含return语句。如果不需要从被调用函数带回函数值可以不要return语句。一个函数中可以有一个以上的return语句

194、,执行到哪一个return语句,哪一个语句起作用。return语句后面的 括弧也可以不要,如:“rrrreeeettttuuuurrrrnnnn;”等价于“rrrreeeettttuuuurrrrnnnn ();”return语句将被调用函数中的一个确定值带回主调函 数中去。见图8.2中从return语句返回的箭头。return语句将被调用函数中的一个确定值带回主调函 数中去。见图8.2中从return语句返回的箭头。如果需要从被调用函数带回一个函数值供主调函数使 用,被调用函数中必须包含return语句。如果不需要从 被调用函数带回函数值可以不要return语句。一个函数中可以有一个以上的r

195、eturn语句,执行到哪一 个return语句,哪一个语句起作用。return语句后面的 括弧也可以不要,如:“return;”等价于“return ();”return后面的值可以是一个表达式。 例如,例8中的函数可以改写成:return后面的值可以是一个表达式。 例如,例8中的函数可以改写成:(int ,int )(?);(2) 函数的返回值应当属于某一个确定的类 型,在定义函数时指定函数返回值的类型。例如:下面是3个函数的首行:intmax(float ,float ) /* 函数值为整型 */ charletter(char c1,char c2)/* 函数值为字符型 */ doubl

196、emin(int ,int )/* 函数值为双精度型 */例如例如例如例如:下面是下面是下面是下面是3333个函数的首行:个函数的首行:个函数的首行:iinnnnttttmmmmaxaxaxax(float ,float )/* 函数值为整型 */ cccchhhhararararllleeeetttttttteeeerrrr(char c1,char c2) /* 函数值为字符型 */ ddddououououbbbbllleeeemmmmiinnnn(int ,int )/* 函数值为双精度型 */在语言中,凡不加类型说明的函数,自动按整型处 理。例8.中的函数首行的函数类型int可以省

197、写,用Turbo C 2.0编译程序时能通过,但用Turbo C 3.0编译程序时不能通过,因为C+要求所有函数 都必须指定函数类型。因此,建议在定义时对所有函数 都指定函数类型。在语言中,凡不加类型说明的函数,自动按整型处理。例8.中的函数首行的函数类型int可以省 写,用Turbo C 2.0编译程序时能通过,但用Turbo C 3.0编译程序时不能通过,因为C+要求所有函数 都必须指定函数类型。因此,建议在定义时对所有函数 都指定函数类型。如果函数值的类型和return语句中表达式的值不一致, 则以函数类型为准。对数值型数据,可以自动进行类 型转换。即函数类型决定返回值的类型。(3)在定

198、义函数时指定的函数类型一般应该和 return语句中的表达式类型一致。如果函数值的类型和return语句中表达式的值不一致, 则以函数类型为准。对数值型数据,可以自动进行类 型转换。即函数类型决定返回值的类型。(4)对于不带回值的函数,应当用“void”定义 函数为“无类型”(或称“空类型”)。这样,系 统就保证不使函数带回任何值,即禁止在调用 函数中使用被调用函数的返回值。此时在函数 体中不得出现return语句。# iiinnnnccccllluuuuddddeeee voivoiddd mmmmainnnn()iinnnntttt (flllloatoatoatoat ,fllloato

199、atoatoat ); fflllloatoatoatoat ,;iinnnntttt ; ssssccccananananf(,); (,);pppprrrriinnnnttttf( ,);iinnnntttt mmmmaxaxaxax(fllloatoatoatoat ,flloatoatoatoat )fflllloatoatoatoat ;/* zzzz为实型变量 */?;rrrreeeettttuuuurrrrnnnn();函数类型不同lllloatoatoatoat );例 8. 返回值类型与# include void main()int (float ,f float ,;in

200、t ;运行情况如下: , Max isscanf(,);(,);printf( ,);int max(float ,float )float ;/* z为实型变量 */?;return();如果实参表列包含多个实参,则各参数 间用逗号隔开。实参与形参的个数应相 等,类型应匹配。实参与形参按顺序对 应,一一传递数据。8.函数的调用8. 函数调用的一般形式如果是调用无参函数,则“实参表列” 可以没有,但括弧不能省略。函数调用的一般形式为:函数名(实参表列)如果是调用无参函数,则“实参表列” 可以没有,但括弧不能省略。如果实参表列包含多个实参,则各参数 间用逗号隔开。实参与形参的个数应相 等,类型应

201、匹配。实参与形参按顺序对 应,一一传递数据。如果实参表列包括多个实参,对实参求值 的顺序并不是确定的,有的系统按自左至 右顺序求实参的值,有的系统则按自右至 左顺序。许多版本是按自右而左的顺序 求值,例如Tubro +。如果实参表列包括多个实参,对实参求值的顺序并不是确定的,有的系统按自左至 右顺序求实参的值,有的系统则按自右至 左顺序。许多版本是按自右而左的顺序 求值,例如Tubro +。#iiinnnncccclluuuuddddeeee vvvvooooiidddd mmmmaaaaiinnnn()iiiinnnntttt ff(iinnnntttt aaaa,iinnnntttt bb

202、bb);/* 函数声明 */ iiiinnnntttt iii=222,pppp;pppp=ff(ii,+iii);/*函数调用 */ pppprrrriinnnnttttf(%d%d%dnnnn,pppp);例 8 实参求值的顺序#include void main()int f(int a,int b);/* 函数声明 */ int i=2,p;p=f(i,+i);/*函数调用 */printf(%dn,p);iinnnntttt f(iinnnntttt aaaa,iiiinnnntttt bbbb)/*函数定义 */iinnnntttt cccc; iiff(aaaabbbb) ccc

203、c=1111;eeeelllsssseeee iif(aaaa=bbbb) cccc=0000; eeeelllsssseeee cccc=-1111;rrrreeeettttuuuurrrrnnnn(cccc);int f(int a,int b)int c; if(ab) c=1;else if(a=b) c=0; else c=-1;return(c);/*函数定义 */对于函数调用int i=2,p;如果按自左至右顺序求实 参的值,则函数调用相当 于(,)p=f(i,+i);如果按自右至左顺序求实 参的值,则函数调用相当 于(3,)函数语句 把函数调用作为一个语句。如例8.1中的pri

204、ntstar(),这时不要求函数带回值,只要求函数完成一定的操作。函数表达式 函数出现在一个表达式中,这种表达式称为函数表达 式。这时要求函数带回一个确定的值以参加表达式的 运算。例如:*(,);8.函数调用的方式函数语句 把函数调用作为一个语句。如例8.1中的printstar(),这 时不要求函数带回值,只要求函数完成一定的操作。函数表达式 函数出现在一个表达式中,这种表达式称为函数表达 式。这时要求函数带回一个确定的值以参加表达式的 运算。例如:*(,);函数参数 函数调用作为一个函数的实参。例如:mmmm = mmmmaaaaxxxx (aaaa , mmmmaaaaxxxx ( bb

205、bb , cccc ) ) ;其中max ( b , c )是一次函数调用,它的值作为max另 一次调用的实参。m的值是a、b、c三者中的最大者。 又如:pppprrrriiinnnnttttf (%d%d%d%d, mmmmaaaaxxxx (aaaa,bbbb);也是把max ( a , b ) 作为printf函数的一个参数。函数调用作为函数的参数,实质上也是函数表达 式形式调用的一种,因为函数的参数本来就要求是表 达式形式。函数参数函数调用作为一个函数的实参。例如:m = max (a , max ( b , c ) ) ;其中max ( b , c )是一次函数调用,它的值作为max

206、另 一次调用的实参。m的值是a、b、c三者中的最大者。 又如:printf (%d, max (a,b);也是把max ( a , b ) 作为printf函数的一个参数。函数调用作为函数的参数,实质上也是函数表达 式形式调用的一种,因为函数的参数本来就要求是表 达式形式。(1) 首先被调用的函数必须是已经存在的 函数(是库函数或用户自己定义的函 数)。但光有这一条件还不够。8.对被调用函数的声明和函数原型(1) 首先被调用的函数必须是已经存在的 函数(是库函数或用户自己定义的函 数)。但光有这一条件还不够。(2) 如果使用库函数,还应该在本文件开 头用命令将调用有关 库函数时所需用到的信息“

207、包含”到本文件 中来。(2) 如果使用库函数,还应该在本文件开头用命令将调用有关 库函数时所需用到的信息“包含”到本文件 中来。(3) 如果使用用户自己定义的函数,而该 函数的位置在调用它的函数(即主调函数)的后面(在同一个文件中),应该 在主调函数中对被调用的函数作声明。(3) 如果使用用户自己定义的函数,而该函数的位置在调用它的函数(即主调函数)的后面(在同一个文件中),应该 在主调函数中对被调用的函数作声明。函数原型的一般形式为(1)函数类型函数名(参数类型1,参数 类型2);(2)函数类型函数名(参数类型1,参数名1,参数类型2,参数名2);函数原型的一般形式为(1)函数类型函数名(参

208、数类型1,参数 类型2);(2)函数类型函数名(参数类型1,参数名1,参数类型2,参数名2);“声明”一词的原文是delaration,过 去在许多书中把它译为“说明”。声明的作用是把函数名、函数参数的个数和 参数类型等信息通知编译系统,以便在遇到函数调用时,编译系统能正确识别 函数并检查调用是否合法。 (例如函数 名是否正确,实参与形参的类型和个数 是否一致)。“声明”一词的原文是delaration,过去在许多书中把它译为“说明”。声明 的作用是把函数名、函数参数的个数和参数类型等信息通知编译系统,以便在 遇到函数调用时,编译系统能正确识别 函数并检查调用是否合法。 (例如函数 名是否正确

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

210、按此进行对照检查。例8 对被调用的函数作声明# iiinnnnccccllluuuuddddeeee voivoiddd mmmmainnnn()flllloatoatoatoatadaddddd(fllloatoatoatoat xxxx, fllloatoatoatoat yyyy);*对被调用函数adadaddddd的声明* fflllloatoatoatoataaaa,bbbb,cccc; ssssccccananananf(f,f,aaaa,bbbb); ccccadadaddddd(aaaa,bbbb);pppprrrriinnnnttttf(ssssuuuummmm iissss

211、 fnnnn,cccc);fflllloatoatoatoat adadddd(fflllloatoatoatoat ,ffllloatoatoatoat )*函数首部*flllloatoatoatoat ;/*函数体 */ zzzz;rrrreeeettttuuuurrrrnnnn(zzzz);# include void main()floatadd(float x, float y);*对被调用函数add的声明* floata,b,c; scanf(f,f,a,b); cadd(a,b);printf(sum is fn,c);float add(float ,float )*函数首部*

212、float ;/*函数体 */ z;return(z);如果被调用函数的定义出现在主调函数之前,可以不必加以声明。因为编译系 统已经先知道了已定义函数的有关情 况,会根据函数首部提供的信息对函数 的调用作正确性检查。如果如果 被调用函数的定义出现在主调函数被调用函数的定义出现在主调函数之前,可以不必加以声明。因为编译系之前,可以不必加以声明。因为编译系 统已经先知道了已定义函数的有关情统已经先知道了已定义函数的有关情 况,会根据函数首部提供的信息对函数况,会根据函数首部提供的信息对函数 的调用作正确性检查。的调用作正确性检查。如果被调用函数的定义出现在主调函数之前,可以不必加以声明。因为编译系

213、 统已经先知道了已定义函数的有关情 况,会根据函数首部提供的信息对函数 的调用作正确性检查。# iiiinnnnccccllluuuuddddeeee fllloatoatoatoat adadaddddd(flllloatoatoatoat ,flloatoatoatoat )*函数首部*flloatoatoatoat ;/*函数体 */ zzzz;rrrreeeettttuuuurrrrnnnn(zzzz);voivoivoivoidddd mmmmainnnn()flloatoatoatoataaaa,bbbb,cccc; ssssccccananananf(f,f,aaaa,bbbb)

214、; ccccadaddddd(aaaa,bbbb);pppprrrriiinnnnttttf(ssssuuuummmm iissss fnnnn,cccc);改写例 8.# include float add(float ,float )*函数首部*float ;/*函数体 */ z;return(z);void main()floata,b,c; scanf(f,f,a,b); cadd(a,b);printf(sum is fn,c);嵌套定义就是在定义一个函数时,其函数体 内又包含另一个函数的完整定义 。语言不能嵌套定义函数,但可以嵌套调用 函数,也就是说,在调用一个函数的过程 中,又调

215、用另一个函数。8. 函数的嵌套调用嵌套定义就是在定义一个函数时,其函数体 内又包含另一个函数的完整定义 。语言不能嵌套定义函数,但可以嵌套调用 函数,也就是说,在调用一个函数的过程 中,又调用另一个函数。例8.用弦截法求方程 f(x)=x3-5x2+16x-80=0的根方法:(1) 取两个不同点x1,x2,如果f(x1)和f(x2)符号相 反,则(x1,x2)区间内必有一个根。如果f(x1)与 f(x2)同符号,则应改变x1,x2,直到f(x1)、f(x2)异 号为止。注意x1、x2的值不应差太大,以保证 (x1,x2)区间内只有一个根。(1) 取两个不同点x1,x2,如果f(x1)和f(x2

216、)符号相(2) 连接(x1,f(x1)和(x2,f(x2)两点,此线(即 弦)交x轴于x。反,则(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。(3) 若f(x)与f(x1)同符号,则根必在(x,x2)区间内,此时将x作为新的x1。如果f(x)与f(x2) 同符号,则表示根在(x1,x)区间内,将x作为新 的x2。(3) 若f(x)与f(x1)同符号,则根必在(x,x

217、2)区(4)重复步骤 (2) 和 (3) , 直到 f(x) 为止, 为一个很小的数, 例如 10-6. 此时认为 f(x)0间内,此时将x作为新的x1。如果f(x)与f(x2) 同符号,则表示根在(x1,x)区间内,将x作为新 的x2。(4)重复步骤 (2) 和 (3) , 直到 f(x) 为止, 为一个很小的数, 例如 10-6. 此时认为 f(x)0N-S流程图(1) 用函数f(x)代表x的函数:x3-5x2+16x-80.(2) 用函数调用xpoint (x1,x2)来求(x1,f(x1)和 (x2,f(x2)的连线与x轴的交点x的坐标。(3) 用函数调用root (x1,x2)来求(

218、x1,x2)区间的 那个实根。显然,执行root函数过程中要用 到函数xpoint,而执行xpoint函数过程中要用 到f函数。分别用几个函数来实现各部分功能:(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函数。fflllooooaaaatttt f(fllooooaaaatttt xxxx)

219、* 定义函数,以实现f(xxxx) xxxx33-5xxxx222+11116666xxxx-80000 * ;=(-.)*+.)*-.;();float f(float x)* 定义函数,以实现f(x) x3-5x2+16x-80 * ;= (-.)*+.)*-.;();ffllooooaaaatttt xxxxppppooooiiiinnnntttt(flllooooaaaatttt xxxx1111,ffllooooaaaatttt xxxx22)*定义xxxxppppooooiinnnntttt函数,求出弦与xxxx轴交点 */ ;=(*()-*()()-(); ();float xp

220、oint(float x1,float x2)*定义xpoint函数,求出弦与x轴交点 */ ;=(*()-*()()-(); ();fllloatoatoatoat rrrrootootootoot(flloatoatoatoat ,fllloatoatoatoat )/* 定义rrrrootootootoot函数,求近似根 */ ,; ();ddddoooo(,); ();iif(*)/*()与()同符号 */; ; ; wwwwhhhhiillleeee().); ();float root(float ,float )/* 定义root函数,求近似根 */ ,; ();do(,); (

221、);if(*)/*()与()同符号 */; ; ; while().); ();voivoivoidddd mmmmainnnn()主函数ffllloatoatoatoat ,;ddddoooopppprrrriinnnnttttf( ,:); ssssccccananananf(,); ();();wwwwhhhhiillleeee(*); (,);pppprrrriiinnnnttttf( rrrrootootootoot of eeeeqqqquuuuatatiionononon iissss.nnnn,);void main()主函数float ,;doprintf( ,:); sca

222、nf(,); ();();while(*); (,);rrrrootootootoot ofofofof eeeeqqqquuuuatatatatiiiionononon iiiissss.nnnn,)printf(;运行情况如下: ,: , root of equation is 8函数的递归调用在调用一个函数的过程中又出现直接或间接 地调用该函数本身,称为函数的递归调用。 语言的特点之一就在于允许函数的递归调 用。例如:在调用一个函数的过程中又出现直接或间接 (iinnnntttt ) ,; (); rrrreeeettttuuuurrrrnnnn(*);地调用该函数本身,称为函数的递归调

223、用。 语言的特点之一就在于允许函数的递归调 用。例如: (int ) ,; (); return(*);例8 有个人坐在一起,问第个人多少岁? 他说比第个人大岁。问第个人岁数,他说比第 个人大岁。问第个人,又说比第个人大岁。 问第个人,说比第个人大岁。最后问第个 人,他说是岁。请问第个人多大。()() ()() ()() ()() () 可以用数学公式表述如下: ()() ()()()() ()() ()() ()() () 可以用数学公式表述如下: ()() ()()可以用一个函数来描述上述递归过程:iiinnnntttt ageageageage(iinnnntttt )*求年龄的递归函数

224、*iinnnntttt ; * 用作存放函数的返回值的变量 * iiif() ;eeeellllsssseeee();rrrreeeettttuuuurrrrnnnn(); 用一个主函数调用ageageageage函数,求得第5人的年龄。#i#i#i#innnncccclluuuuddddeeee voivoidddd mmmmainnnn() (,();可以用一个函数来描述上述递归过程:int age(int )*求年龄的递归函数*int ; * 用作存放函数的返回值的变量 * if() ;else();return(); 用一个主函数调用age函数,求得第5人的年龄。运行结果如下: #in

225、clude void main() (,();例8.用递归方法求!求!也可以用递归方法,即!等于 !,而!。可用下面的递归公式表示: !(,)()!()求!也可以用递归方法,即!等于!,而!。可用下面的递归公式表示: !(,) ()!()例8.(汉诺)塔问题。这是一个古典的数学问题,是一个用递归方法解 题的典型例子。问题是这样的:古代有一个梵 塔,塔内有3个座A、B、C,开始时座上有 个盘子,盘子大小不等,大的在下,小的在 上(见图8)。有一个老和尚想把这 个盘子从座移到座,但每次只允许移动 一个盘,且在移动过程中在3个座上都始终保 持大盘在下,小盘在上。在移动过程中可以利 用座,要求编程序打

226、印出移动的步骤。(1) 将座上个盘子移到座上(借助);(2) 将座上个盘子移到座上;(3) 将座上个盘子移到座上(借助)。 其中第()步可以直接实现。第步又可用递归方 法分解为: 将上个盘子从移到; 将上个盘子从移到; 将上个盘子从移到。 第()步可以分解为: 将上个盘子从移到上; 将上个盘子从移到上; 将上个盘子从移到上。将以上综合起来,可得到移动座上的过程:3个盘子的步骤为 , ,(1) 将座上个盘子移到座上(借助);。为便于理解,我们先分析将座上个盘子移到 将以上综合起来,可得到移动3个盘子的步骤为 , ,。(2) 将座上个盘子移到座上;(3) 将座上个盘子移到座上(借助)。 其中第()

227、步可以直接实现。第步又可用递归方 法分解为: 将上个盘子从移到; 将上个盘子从移到; 将上个盘子从移到。 第()步可以分解为: 将上个盘子从移到上; 将上个盘子从移到上; 将上个盘子从移到上。由上面的分析可知:将个盘子从座移 到座可以分解为以下3个步骤:(1) 将上个盘借助座先移到座上。(2) 把座上剩下的一个盘移到座上。(3) 将个盘从座借助于座移到 座上。由上面的分析可知:将个盘子从座移 到座可以分解为以下3个步骤:(1) 将上个盘借助座先移到座上。(2) 把座上剩下的一个盘移到座上。(3) 将个盘从座借助于座移到 座上。程序如下:#i#i#i#innnnccccllluuuuddddee

228、ee voivoiddd mmmmainnnn()voivoiddd hhhhananananoi(iinnnntttt nnnn,c,c,c,chhhharararar ononononeeee,c,c,c,chhhharararar ttttwwwwo,co,co,co,chhhharararar tttthhhhrrrreeeeeeee);/* 对hhhhananananoi函数的声明 */iinnnntttt mmmm;pppprrrriinnnnttttf(iinnnnppppuuuutttt tttthhhheeee nnnnuuuummmmbbbbeeeerrrr of dddd

229、iiisssskkkkeeeessss:); ssssccccananananf(“%d%d%d%d”,&,&,&,&mmmm);pppprrrriinnnnttttf(TThhhheeee sssstttteeeepppp ttttoooo mmmmoveoveoveoveiiiinnnngggg %d%d%d%d ddddiisssskkkkeeeessss:nnnn,m,m,m,m); hhhhananananoi(mmmm,AAAA,B,CCCC);程序如下:#include void main()void hanoi(int n,char one,char two,char thre

230、e);/* 对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);voivoivoivoidddd hhhhananananoi(iinnnntttt nnnn,c,c,c,chhhharararar ononononeeee,c,c,c,chhhharararar ttttwwwwo,co,co,co,chhhharararar tttthhhhrrrreeeeeeee)/*定义hhhha

231、nanananoi函数,将个盘从ononononeeee座借助ttttwwwwoooo座,移到tttthhhhrrrreeeeeeee座 */voivoivoivoidddd mmmmoveoveoveove(cccchhhharararar x,cx,cx,cx,chhhharararar y)y)y)y);/* 对mmmmoveoveoveove函数的声明 */iif(nnnn=1)1)1)1) mmmmoveoveoveove(ononononeeee,t,t,thhhhrrrreeeeeeee); eeeellsssseeeehhhhananananoi(nnnn-1,on1,on1,

232、on1,oneeee,t,t,thhhhrrrreeeeeeee,t,t,t,twwwwo)o)o)o); mmmmoveoveoveove(ononononeeee,t,t,thhhhrrrreeeeeeee);hhhhananananoi(nnnn-1,t1,t1,t1,twwwwo,ono,ono,ono,oneeee,t,t,t,thhhhrrrreeeeeeee);voivoivoivoidddd mmmmoveoveoveove(cccchhhharararar x,cx,cx,cx,chhhharararar y)y)y)y)/*定义mmmmoveoveoveove函数 */pp

233、pprrrriinnnnttttf(“%c%c%c%c-%c%c%c%cnnnn,x,y),x,y),x,y),x,y);void hanoi(int n,char one,char two,char three)/*定义hanoi函数,将个盘从one座借助two座,移到three座 */void move(char x,char y);/* 对move函数的声明 */if(n=1) move(one,three); elsehanoi(n-1,one,three,two); move(one,three);hanoi(n-1,two,one,three);void move(char x,c

234、har y)/*定义move函数 */printf(“%c-%cn,x,y);运行情况如下:iiinnnnppppuuuutttt tttthhhheeee nnnnuuuummmmbbbbeeeerrrr of ddddiiisssskkkkeeeessss:33TThhhheeee sssstttteeeeppppssss ttttoooo nnnnovioviovinnnngggg 33 ddddiiisssskkkkeeeessss: 运行情况如下:input the number of diskes:3The steps to noving 3 diskes: 8.数组作为函数参数8

235、.7.1 数组元素作函数实参由于实参可以是表达式,而数组元素可以是表达式 的组成部分,因此数组元素当然可以作为函数的实 参,与用变量作实参一样,是单向传递,即“值传送” 方式。由于实参可以是表达式,而数组元素可以是表达式 例8.8.8.8. 有两个数组和,各有个元素,将的组成部分,因此数组元素当然可以作为函数的实 它们对应地逐个相比(即与比,参,与用变量作实参一样,是单向传递,即“值传送” 与比)。如果数组中的元方式。 素大于数组中的相应元素的数目多于bbbb数组中元素大于aaaa数组中相应元素的数目(例如,aaaaiiibbbbii6666 次,bbbbiiiaaaaii333次,其中ii每

236、次为不同的值),则 认为aaaa数组大于bbbb数组,并分别统计出两个数组相应 元素大于、等于、小于的次数。例8. 有两个数组和,各有个元素,将它们对应地逐个相比(即与比, 与比)。如果数组中的元 素大于数组中的相应元素的数目多于b数组中元素 大于a数组中相应元素的数目(例如,aibi6 次,biai3次,其中i每次为不同的值),则 认为a数组大于b数组,并分别统计出两个数组相应 元素大于、等于、小于的次数。#i#i#i#innnnccccllluuuuddddeeee voivoivoivoidddd mmmmainnnn()iinnnntttt llarararargegegege(iin

237、nnntttt xxxx,iinnnntttt y)y)y)y);/*函数声明 */iinnnntttt 1010,1010,,; pppprrrriiinnnnttttff(eeeennnntttteeeerrrr ararararrrrrayayayay aaaa); forororor(;) ssssccccananananf(,); pppprrrriiinnnnttttf();pppprrrriiinnnnttttff( eeeennnntttteeeerrrr ararararrrrrayayayay); forororor(;) ssssccccananananf (,); pp

238、pprrrriiiinnnnttttff(); forororor(;)iif(llllarararargegegege (ii,ii )= );eeeelllsssseeee iiff( llllarararargegegege (ii,ii )=) =+;eeeelllsssseeee ;#include void main()int large(int x,int y);/*函数声明 */int 10,10,,; printf(enter array a); for(;) scanf(,); printf();printf( enter array); for(;) scanf (,);

239、 printf(); for(;)if(large (i,i )= );else if( large (i,i )=) =+;else ;pppprrrriinnnnttttf(aaiibbbbii %d%d%d ttttiimmmmeeeessssnnnnaii=bbbbii %d%d%d%d ttttiimmmmeeeessssnnnnaiiikkkk) pppprrrriiinnnnttttf(ararararrrrrayayayay aaaa iissss llarararargegegegerrrr tttthhhhanananan ararararrrrrayayayay bbbb

240、nnnn);eeeellsssseeee iiff (nnnnbi %d timesnai=bi %d timesnaik) printf(array a is larger than array bn);else if (nk) printf(array a is smaller than array bn); elseprintf(array is equal to array bn);large(int ,int )int ; if(); else if()flag;else flag;return(flag);运行情况如下:enter array a: 5 3 8 9 1 3 5 6

241、0 4 array a is smaller thann array b可以用数组名作函数参数,此时形参应当用数组名 或用指针变量 。8.7.2 数组名作函数参数可以用数组名作函数参数,此时形参应当用数组名 或用指针变量 。例8.11 有一个一维数组,内放 个学生成绩,求平均成绩。#i#i#i#innnncccclluuuuddddeeee voivoidddd mmmmainnnn() fllloatoatoatoataveaveaveaverrrrageageageage(flllloatoatoatoat ararararrrrrayayayay1010);/* 函数声明 */flllo

242、atoatoatoat ssssccccororororeeee1010 , aveaveaveaverrrr; iiinnnntttt ;pppprrrriiinnnnttttf(iiinnnnppppuuuutttt ssssccccororororeeeessss:); forororor(; ssssccccananananf(,ssssccccororororeeee); pppprrrriiinnnnttttf();aveaveaveaverrrraveaveaveaverrrrageageageage( ssssccccororororeeee );pppprrrriiinnnn

243、ttttf ( aveaveaveaverrrrageageageage ssssccccororororeeee iissss .nnnn, aveaveaveaverrrr);#include void main() floataverage(float array10);/* 函数声明 */ float score10 , aver;int ;printf(input scores:); for(; scanf(,score); printf();averaverage( score );printf ( average score is .n, aver);ffllooooaaaatt

244、ttaaaavvvveeeerrrraaaaggggeeee (flllooooaaaatttt aaaarrrrrrrraaaayyyy11110000) iinnnntttt ;flllooooaaaattttaaaavvvveeeerrrr,;foooorrrr(;) ; ; rrrreeeettttuuuurrrrnnnn(aaaavvvveeeerrrr);floataverage (float array10) int ;floataver,;for(;) ; ; return(aver);运行情况如下:input scores: .5 .5 average score is 83

245、.40例 8.形参数组不定义长度#i#i#i#innnncccclluuuuddddeeee voivoivoivoiddddmmmmainnnn()fllloatoatoatoataveaveaveaverrrrageageageage(ffllloatoatoatoat ,iinnnntttt )fllloatoatoatoatssssccccororororeeee_1_1_1_15 ,.,;fllloatoatoatoat ssssccccororororeeee_2_2_2_21010= 67.567.567.5,89.589.5,99999999,666.5,77777777,89

246、.5,76.576.576.576.5,545454,60606060,99.599.599.599.5;pppprrrriinnnnttttff(“tttthhhheeee aveaveaveaverrrrageageageage of cccclllasasasasssss AAAA iissss %6.2f%6.2f%6.2f%6.2fnnnn”, aveaveaveaverrrrageageageage(ssssccccororororeeee_1_1_1_1,5)5)5);pppprrrriiinnnnttttf(“tttthhhheeee aveaveaveaverrrrageag

247、eageage of cccclllasasasasssss B iiissss %6.2f%6.2f%6.2f%6.2fnnnn”, aveaveaveaverrrrageageageage(ssssccccororororeeee_2_2_2_2,10);#include voidmain()floataverage(float ,int )floatscore_15 ,.,;float score_210= 67.5,89.5,99,6.5,77,89.5,76.5,54,60,99.5;printf(“the average of class A is %6.2fn”, average

248、(score_1,5);printf(“the average of class B is %6.2fn”, average(score_2,10);fllooooaaaattttaaaavvvveeeerrrraaaaggggeeee(ffllllooooaaaatttt ,iinnnntttt iinnnntttt ;flllooooaaaattttaaaavvvveeeerrrr,; foooorrrr(; ssssuuuummmmssssuuuummmmaaaarrrrrrrraaaayyyy; aaaavvvveeeerrrrssssuuuummmm;rrrreeeettttuuuu

249、rrrrnnnn();floataverage(float ,int )int ;floataver,; for(; sumsumarray; aversum;return();运行结果如下:the average of class A is 80.40 The average of class is78.20例 8.13 用选择法对数组中10个整数按由小到大排序。所谓选择法就是先将10个数 中最小的数与a0对换;再将a1到a9中最小的数与a1对换每比较一轮,找出一个未经排序的数中最小的 一个。共比较9轮。a0aa1111a2222aa3a4444333666611119994444未排序时的

250、情况11116666339994444将5个数中最小的数1111与aaaa0000对换11113336666994444将余下的4444个数中最小的数33与aaaa1111对换111133344449996666将余下的333个数中最小的数4444与aaaa22对换111133444466699将余下的222个数中最小的数6666与aaaa33对 换,至此完成排序a0a1a2a3a436194未排序时的情况16394将5个数中最小的数1与a0对换13694将余下的4个数中最小的数3与a1对换13496将余下的3个数中最小的数4与a2对换13469将余下的2个数中最小的数6与a3对 换,至此完成

251、排序程序实例#i#i#i#innnncccclluuuuddddeeee voivoiddddmmmmainnnn()voivoidddssssorororortttt(iinnnntttt ,iinnnntttt ); iiinnnntttt ,;pppprrrriiinnnnttttf(eeeennnntttteeeerrrrtttthhhheeeeararararrrrrayayayay);forororor(; ssssccccananananf(,); ssssorororortttt(,);pppprrrriiinnnnttttf(tttthhhheeeessssorororort

252、ttteeeeddd ararararrrrrayayayay); fforororor(;pppprrrriinnnnttttf(,); pppprrrriinnnnttttf();程序实例#include voidmain()voidsort(int ,int ); int ,;printf(enterthearray);for(; scanf(,); sort(,);printf(thesorted array);for(; printf(,); printf();voivoidddssssorororortttt(iinnnntttt ,iinnnntttt )iinnnntttt ,

253、;forororor(;);forororor(;) iiiff(ararararrrrrayayay ararararrrrrayay=; =ararararrrrrayayayaykkkk;ararararrrrrayayayaykkkk=ararararrrrrayayiii;ararararrrrrayayiii=ttttvoidsort(int ,int )int ,;for(;);for(;) if(array array=; =arrayk;arrayk=arrayi;arrayi=t8.7.3. 多维数组名作函数参数程序如下:#iiinnnncccclllluuuuddddee

254、ee vvvvooooiiiddddmmmmaaaaiiinnnn() mmmmaaaaxxxx_vvvvaaaallluuuueeee ( iinnnntttt 4444);iinnnntttt 334444=1111,33,5,7777,22,4444,6666,8,11115,11117777,3334444,111122;pppprrrriiinnnnttttf(mmmmaaaaxxxx vvvvaaaalluuuueeee iissss ,mmmmaaaaxxxx_vvvvaaaallluuuueeee(aaaa) );程序如下:#include voidmain() max_val

255、ue ( int 4);int 34=1,3,5,7,2,4,6,8,15,17,34,12;printf(max value is ,max_value(a) );mmmmaaaaxxxx_vvvvaaaalluuuueeee (iinnnntttt aaaarrrrrrrraaaayyyy 4444)iiiinnnntttt ,; =; (=;)(;(aaaarrrrrrrraaaayyyy) = aaaarrrrrrrraaaayyyy ;rrrreeeettttuuuurrrrnnnn();max_value (int array 4)int ,; =; (=;)(;(array) =

256、 array ;return();运行结果如下:Max value is 348.8局部变量和全局变量在一个函数内部定义的变量是内部变量,它只在本 函数范围内有效,也就是说只有在本函数内才能使 用它们,在此函数以外是不能使用这些变量的。这 称为“局部变量”。8.8.1局部变量在一个函数内部定义的变量是内部变量,它只在本 函数范围内有效,也就是说只有在本函数内才能使 用它们,在此函数以外是不能使用这些变量的。这称为“局部变量”。fllllooooaaaatttt ff1111( iinnnntttt aaaa)/*函数f1111*/iinnnntttt bbbb,cccc;aaaa、bbbb、c

257、ccc有效cccchhhhaaaarrrr f222(iinnnntttt xxxx,iiinnnntttt yyyy)/*函数f222 */iinnnntttt iii,jjjj;xxxx、yyyy、ii、jjjj有效vvvvooooiiidddd mmmmaaaaiinnnn( )/*主函数*/iiinnnntttt mmmm,nnnn;mmmm、nnnn有效float f1( int a)int b,c;/*函数f1*/a、b、c有效char f2(int x,int y)/*函数f2 */int i,j;x、y、i、j有效void main( )/*主函数*/int m,n;m、n有效

258、(1) 主函数中定义的变量(m,n)也只在主函数中有效,而不因为在主函数中定义而在整个文件或程序中有 效。主函数也不能使用其他函数中定义的变量。(2) 不同函数中可以使用相同名字的变量,它们代表不同的对象,互不干扰。例如, 上面在f1函数中定义了变 量b和c,倘若在f2函数中也定义变量b和c,它们在内存 中占不同的单元,互不混淆。(3) 形式参数也是局部变量。例如上面f1函数中的形参a,也只在f1函数中有效。其他函数可以调用f1函 数,但不能引用f1函数的形参a。(4) 在一个函数内部,可以在复合语句中定义变量,这 些变量只在本复合语句中有效,这种复合语句也称为“分程序”或“程序块”。(1)

259、主函数中定义的变量(m,n)也只在主函数中有效, 而不因为在主函数中定义而在整个文件或程序中有 效。主函数也不能使用其他函数中定义的变量。说明(2) 不同函数中可以使用相同名字的变量,它们代表不 同的对象,互不干扰。例如, 上面在f1函数中定义了变 量b和c,倘若在f2函数中也定义变量b和c,它们在内存 中占不同的单元,互不混淆。(3) 形式参数也是局部变量。例如上面f1函数中的形 参a,也只在f1函数中有效。其他函数可以调用f1函 数,但不能引用f1函数的形参a。(4) 在一个函数内部,可以在复合语句中定义变量,这 些变量只在本复合语句中有效,这种复合语句也称为 “分程序”或“程序块”。vv

260、vvooooiidddd mmmmaaaaiinnnn ( )iinnnntttt aaaa,bbbb;iinnnntttt cccc;cccc=aaaa+bbbb;cccc在此范围内有效aaaa,bbbb在此范围内有效void main ( )int a,b;int c;c=a+b;c在此范围内有效a,b在此范围内有效8.8.2 全局变量在函数内定义的变量是局部变量,而在函数之 外定义的变量称为外部变量,外部变量是全局变量(也称全程变量)。全局变量可以为本文件 中其他函数所共用。它的有效范围为从定义 变量的位置开始到本源文件结束。在函数内定义的变量是局部变量,而在函数之外定义的变量称为外部变

261、量,外部变量是全局 变量(也称全程变量)。全局变量可以为本文件中其他函数所共用。它的有效范围为从定义 变量的位置开始到本源文件结束。iinnnntttt pppp=1,q1,q1,q1,q=5;5;5;5;/* 外部变量 */flllloatoatoatoat f1(1(1(1(iinnnntttt a)a)a)/* 定义函数f1111 */iinnnntttt bbbb,c,c,c,c;cccchhhharararar cccc1,c1,c1,c1,c2;2;2;2;/* 外部变量*/ cccchhhharararar f222 (iiinnnntttt x,x,x,x, iinnnnttt

262、t y)y)y)y)/* 定义函数f222 */iinnnntttt ii,j,j,j,j;全局变量pppp,q,q,q,q的作用范围全局变量cccc1,c1,c1,c1,c222的作用范围voivoivoivoidddd mmmmainnnn ( )/*主函数*/iinnnntttt mmmm,n,n,n,n;int p=1,q=5;float f1(int a)int b,c;char c1,c2;char f2 (int x, int y)/* 外部变量 */* 定义函数f1 */* 外部变量*/* 定义函数f2 */int i,j;void main ( )int m,n;全局变量p,

263、q的作用范围全局变量c1,c2的作用范围/*主函数*/例8.15 有一个一维数组,内放个学生成绩,写 一个函数,求出平均分、最高分和最低分。#i#i#i#innnnccccllluuuuddddeeee fllloatoatoatoatMMaxaxaxax,MMiinnnn;*全局变量* voivoivoivoiddddmmmmainnnn()flloatoatoatoataveaveaveaverrrrageageageage(flloatoatoatoat ararararrrrrayayayay ,iiinnnntttt nnnn); fflloatoatoatoataveaveavea

264、ve,ssssccccororororeeee1010;iinnnntttt ;forororor(;) ssssccccananananf(,); aveaveaveave= aveaveaveaverrrrageageageage(,); pppprrrriiinnnnttttff(“mmmmax=ax=ax=ax=%6.2f%6.2f%6.2f%6.2fnnnnmmmmiinnnn=%6.2f%6.2f%6.2f%6.2fnnnnaveaveaveaverrrrageageageage=%6.2f%6.2f%6.2f%6.2fnnnn“,MMaxaxaxax,MMiinnnn,aveav

265、eaveave);#include floatMax,Min;*全局变量* voidmain()floataverage(float array ,int n); floatave,score10;int ;for(;) scanf(,); ave= average(,); printf(“max=%6.2fnmin=%6.2fnaverage=%6.2fn“,Max,Min,ave);flloatoatoatoat aveaveaveaverrrrageageageage(fllloatoatoatoat ararararrrrrayayayay ,iiinnnntttt nnnn)*定义函

266、数,形参为数组 */ iiinnnntttt ;fllloatoatoatoataveaveaveaverrrr,ssssuuuummmm=ararararrrrrayayayay; MMax=ax=ax=ax=MMiinnnn=ararararrrrrayayayay; forororor(=;)iiff(ararararrrrrayayayayMMaxaxaxax)MMaxaxaxaxararararrrrrayayayay;eeeellsssseeeeiiif(ararararrrrrayayayayMiiinnnn)MMiiinnnn ararararrrrrayayayay; sss

267、suuuummmm=ssssuuuummmm+ararararrrrrayayayay;aveaveaveaverrrr; rrrreeeettttuuuurrrrnnnn();float average(float array ,int n)*定义函数,形参为数组 */ int ;floataver,sum=array; Max=Min=array; for(=;)if(arrayMax)Maxarray; );elseif(arrayMin)Min array; sum=sum+array; aver return(运行情况如下: 建议不在必要时不要使用全局变量,原因如下: 全局变量在程序

268、的全部执行过程中都占用存储单 元,而不是仅在需要时才开辟单元。 使用全局变量过多,会降低程序的清晰性,人们 往往难以清楚地判断出每个瞬时各个外部变量的值。 在各个函数执行时都可能改变外部变量的值,程序 容易出错。因此,要限制使用全局变量。 全局变量在程序的全部执行过程中都占用存储单元,而不是仅在需要时才开辟单元。 使用全局变量过多,会降低程序的清晰性,人们 往往难以清楚地判断出每个瞬时各个外部变量的值。 在各个函数执行时都可能改变外部变量的值,程序 容易出错。因此,要限制使用全局变量。它使函数的通用性降低了,因为函数在执行时要 依赖于其所在的外部变量。如果将一个函数移到另 一个文件中,还要将有

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

270、nncccclllluuuuddddeeee iinnnntttt a=a=a=a=3,b3,b3,b3,b=5;5;5;5;/* a,ba,ba,ba,b为外部变量*/a,ba,ba,ba,b作用范围voivoivoiddd mmmmainnnn ( )iinnnntttt a=a=a=a=8;8;8;8;/*aaaa为局部变量 */局部变量aaaa作用范围pppprrrriinnnnttttf (%d%d%d%d, mmmmaxaxaxax (a,ba,ba,ba,b);全局变量bbbb的作用范围mmmmaxaxaxax (iinnnntttt a,a,a,a, iinnnntttt bb

271、bb)/*a,ba,ba,ba,b为局部变量 */iinnnntttt cccc;cccc=aaaabbbb?a?a?a?abbbb;形参aaaa、bbbb作用范围rrrreeeettttuuuurrrrnnnn (cccc);#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 (c);运行结果为88

272、. 变量的存储类别前面已介绍了从变量的作用域(即从空间)角度来 分,可以分为全局变量和局部变量。那么从变量值 存在的时间(即生存期)角度来分,又可以分为静 态存储方式和动态存储方式。8. 动态存储方式与静态存储方式前面已介绍了从变量的作用域(即从空间)角度来 分,可以分为全局变量和局部变量。那么从变量值 存在的时间(即生存期)角度来分,又可以分为静 态存储方式和动态存储方式。所谓静态存储方式是指在程序运行期间由系统分配固定的存储空间的方式。而动态存储方式则是在 程序运行期间根据需要进行动态的分配存储空间的 方式。这个存储空间可以分为三部分:程序区静态存储区动态存储区所谓静态存储方式是指在程序运

273、行期间由系统分配固定的存储空间的方式。而动态存储方式则是在 程序运行期间根据需要进行动态的分配存储空间的 方式。这个存储空间可以分为三部分:程序区静态存储区动态存储区在语言中每一个变量和函数有两个属性: 数据类型和数据的存储类别。对数据类型, 读者已熟悉(如整型、字符型等)。存储类 别指的是数据在内存中存储的方式。存储方式分为两大类:静态存储类和动态存储类。 具体包含四种:自动的(),静态 的(),寄存器的( ),外部的()。根据变量的存储类别,可以知道变量的作用域 和生存期。在语言中每一个变量和函数有两个属性: 数据类型和数据的存储类别。对数据类型,读者已熟悉(如整型、字符型等)。存储类 别

274、指的是数据在内存中存储的方式。存储方式分为两大类:静态存储类和动态存储类。 具体包含四种:自动的(),静态 的(),寄存器的( ),外部的()。根据变量的存储类别,可以知道变量的作用域 和生存期。8. auto变量函数中的局部变量,如不专门声明为static存储类别,都是动态地分配存储空间的,数据存储在动态存储区中。 函数中的形参和在函数中定义的变量(包括在复合语句中 定义的变量),都属此类,在调用该函数时系统会给它们 分配存储空间,在函数调用结束时就自动释放这些存储 空间。因此这类局部变量称为自动变量。自动变量用关 键字作存储类别的声明。例如:int (int ) *定义f函数,为形参 *a

275、utoint ,; *定义、为自动变量 *8.9.3用static声明局部变量有时希望函数中的局部变量的值在函数调用结束后 不消失而保留原值,即其占用的存储单元不释放, 在下一次该函数调用时,该变量已有值,就是上一 次函数调用结束时的值。这时就应该指定该局部变 量为“静态局部变量”,用关键字进行 声明。通过下面简单的例子可以了解它的特点。例87 考察静态局部变量的值。#include voidmain()int (int); ,; (; ( ,();int (int ) int ; ; ; ;();对静态局部变量的说明:(1) 静态局部变量属于静态存储类别,在静态存储区内 分配存储单元。在程序

276、整个运行期间都不释放。而自 动变量(即动态局部变量)属于动态存储类别,占动 态存储区空间而不占静态存储区空间,函数调用结束 后即释放。(2) 对静态局部变量是在编译时赋初值的,即只赋初值 一次,在程序运行时它已有初值。以后每次调用函数 时不再重新赋初值而只是保留上次函数调用结束时的 值。而对自动变量赋初值,不是在编译时进行的,而 是在函数调用时进行,每调用一次函数重新给一次初 值,相当于执行一次赋值语句。(3)如在定义局部变量时不赋初值的话,则对静态局 部变量来说,编译时自动赋初值(对数值型变量) 或空字符(对字符变量)。而对自动变量来说,如 果不赋初值则它的值是一个不确定的值。这是由于 每次

277、函数调用结束后存储单元已释放,下次调用时 又重新另分配存储单元,而所分配的单元中的值是 不确定的。(4) 虽然静态局部变量在函数调用结束后仍然存在, 但其他函数是不能引用它的。例88 输出到的阶乘值。#include voidmain()intfac(int ); int ;for(;)printf(%!=,fac();Int fac(int )staticint ; *; ();8.9.4 register变量一般情况下,变量(包括静态存储方式和动态存储 方式)的值是存放在内存中的。当程序中用到哪一 个变量的值时,由控制器发出指令将内存中该变量 的值送到运算器中。 经过运算器进行运算,如果

278、需要存数,再从运算器将数据送到内存存放。如果有一些变量使用频繁(例如在一个函数中执行次循环,每次循环中都要引用某局部变 量),则为存取变量的值要花费不少时间。为提高 执行效率,语言允许将局部变量的值放在CPU中 的寄存器中,需要用时直接从寄存器取出参加运 算,不必再到内存中去存取。由于对寄存器的存取 速度远高于对内存的存取速度,因此这样做可以提高执行效率。这种变量叫做寄存器变量,用关键字 作声明。例如,例819中的程序是输出到n的阶乘的值。例819使用寄存器变量#include void main ( )long fac(long); long i,n; scanf(%ld,&n); for(

279、i=1;i=n;i+)printf(%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);8.5用extern声明外部变量外部变量是在函数的外部定义的全局变量,它的作 用域是从变量的定义处开始,到本程序文件的末尾。 在此作用域内,全局变量可以为程序中各个函数所 引用。编译时将外部变量分配在静态存储区。有时需要用extern来声明外部变量,以扩展外部变 量的作用城。1. 在一个文件内声明外部变量例820 用extern声明外部变量,扩展它在程 序文件

280、中的作用域。#include void main() int max(int,int);*外部变量声明*externA,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);2. 在多文件的程序中声明外部变量例8 用extern将外部变量的作用域扩展到其他 文件。 本程序的作用是给定的值,输入和,求 和am的值。文件file中的内容为:#include int A;/*定义外部变量*/ void main()int (int);/*函数声明*/int

281、 ,;printf(enter the number a and its power m:n); scanf(,A,); A*; printf(*,A,); (); printf(*n,A,);文件file中的内容为:externA; /*声明A为一个已定义的外部变量*/ int (int );int ,; for(;) *A;();8.9.6用static声明外部变量有时在程序设计中希望某些外部变量只限于被本 文件引用,而不能被其他文件引用。这时可以在 定义外部变量时加一个声明。 例如:file1.cfile2.cstatic int A;extern int A;void main ( )

282、void fun (int n)A=A*n;8.9.7关于变量的声明和定义对变量而言,声明与定义的关系稍微复杂一些。在声 明部分出现的变量有两种情况:一种是需要建立存储 空间的(如:int a; ),另一种是不需要建立存储空间的(如:extern a;)。前者称为“定义性声明”(definingdeclaration) ,或简称定义(definition)。 后者称为 “引用性声明”(referencing declaration)。广义地说,声 明包括定义,但并非所有的声明都是定义。对“int a;” 而言,它既是声明,又是定义。而对“extern a;” 而言,它是声明而不是定义。一般为了

283、叙述方便,把建立存储空间的声明称定 义,而把不需要建立存储空间的声明称为声明。显然这里指的声明是狭义的,即非定义性声明。 例如:void main()extern A;/*是声明不是定义。声明A是一个已定义的外部变量*/int A;8.8存储类别小结(1) 从作用域角度分,有局部变量和全局变量。它们 采用的存储类别如下:局部变量 |自动变量,即动态局部变量(离开函数,值就消失)|静态局部变量(离开函数,值仍保留)|寄存器变量(离开函数,值就消失)|(形式参数可以定义为自动变量或寄存 器变量)全局变量 |静态外部变量(只限本文件引用)|外部变量(即非静态的外部变量,允许其他文件引用)(2) 从变

284、量存在的时间(生存期)来区分,有动态 存储和静态存储两种类型。静态存储是程序整个运 行时间都存在,而动态存储则是在调用函数时临时 分配单元。动态存储|自动变量(本函数内有效)|寄存器变量(本函数内有效)|形式参数(本函数内有效)静态存储 |静态局部变量(函数内有效)|静态外部变量(本文件内有效)|外部变量(其他文件可引用)(3) 从变量值存放的位置来区分,可分为:内存中静态存储区 |静态局部变量|静态外部变量(函数外部静态变量)|外部变量(可为其他文件引用) 内存中动态存储区:自动变量和形式参数 CPU中的寄存器:寄存器变量() 关于作用域和生存期的概念。从前面叙述可以知道,对一个变量的性质可

285、以从两个方面分析,一是 变量的作用域,一是变量值存在时间的长短,即生 存期。前者是从空间的角度,后者是从时间的角度。 二者有联系但不是同一回事。(5) static对局部变量和全局变量的作用不同。对局部 变量来说,它使变量由动态存储方式改变为静态存储方 式。而对全局变量来说,它使变量局部化(局部于本文 件),但仍为静态存储方式。从作用域角度看,凡有static 声明的,其作用域都是局限的,或者是局限于本函数内 (静态局部变量),或者局限于本文件内(静态外部变量)。函数本质上是全局的,因为一个函数要被另外的函数 调用,但是,也可以指定函数不能被其他文件调用。根据函数能否被其他源文件调用,将函数区

286、分为内部函数和外部函数。8.10 内部函数和外部函数函数本质上是全局的,因为一个函数要被另外的函数 调用,但是,也可以指定函数不能被其他文件调用。根据函数能否被其他源文件调用,将函数区分为内部函数和外部函数。如果一个函数只能被本文件中其他函数所调用,它称为内部函数。在定义内部函数时,在函数名和函数类型的前面加static。即ssssttttatatatiicccc 类型标识符 函数名(形参表)如ssssttttatatiiicccc iinnnntttt ffuuuunnnn ( iinnnntttt aaaa , iinnnntttt bbbb )8.10.1内部函数如果一个函数只能被本文件

287、中其他函数所调用,它称 为内部函数。在定义内部函数时,在函数名和函数类型的前面加static。即static 类型标识符 函数名(形参表)如static int fun ( int a , int b )8.10.2外部函数(1)1)1)1) 在定义函数时,如果在函数首部的最左端加关键字eeeextxtxtxteeeerrrrnnnn,则表示此函数是外部函数,可供其他文件调 用。如函数首部可以写为eeeextxtxtxteeeerrrrnnnn iinnnntttt fuuuunnnn (iinnnntttt a,a,a,a, iinnnntttt bbbb) 这样,函数fuuuunnnn就可

288、以为其他文件调用。CCCC语言规 定,如果在定义函数时省略eeeextxtxtxteeeerrrrnnnn,则隐含为外部函 数。本书前面所用的函数都是外部函数。(2)2)2)2) 在需要调用此函数的文件中,用eeeextxtxtxteeeerrrrnnnn对函数作声 明,表示该函数是在其他文件中定义的外部函数(1) 在定义函数时,如果在函数首部的最左端加关键字 extern,则表示此函数是外部函数,可供其他文件调 用。如函数首部可以写为extern int fun (int a, int b) 这样,函数fun就可以为其他文件调用。C语言规 定,如果在定义函数时省略extern,则隐含为外部函

289、 数。本书前面所用的函数都是外部函数。(2) 在需要调用此函数的文件中,用extern对函数作声 明,表示该函数是在其他文件中定义的外部函数例 8.22 有一个字符串,内有若干个字符,今输入一个字 符,要求程序将字符串中该字符删去。用外部函数实现Fiilleeee.c.c.c.c(文件)#i#i#i#innnnccccllluuuuddddeeee voivoivoiddd mmmmainnnn() eeeextxtxtxteeeerrrrnnnn voivoivoivoidddd eeeennnntttteeeerrrr_s_s_s_sttttrrrriiiinnnng(g(cccchhhh

290、arararar ssssttttrrrr);eeeextxtxtxteeeerrrrnnnn voivoivoivoidddd dddeeeetttteeeellleeee_s_s_s_sttttrrrriinnnng(g(cccchhhharararar ssssttttrrrr,c,c,c,chhhharararar cccchhhh);eeeextxtxtxteeeerrrrnnnn voivoivoivoidddd pppprrrriiiinnnntttt_s_s_s_sttttrrrriiiinnnng(g(cccchhhharararar ssssttttrrrr);*以上33行

291、声明在 本函数中将要调用的在其他文件中定义的33个函数*cccchhhharararar cccc;cccchhhharararar ssssttttrrrr80; ssssccccananananf(%c%c%c%c,&,&,&,&cccc); ddddeeeetttteeeellleeee_s_s_s_sttttrrrriinnnng(g(ssssttttrrrr,c,c,c,c); pppprrrriinnnntttt_s_s_s_sttttrrrriinnnng(g(ssssttttrrrr);File.c(文件)#include void main() extern void ent

292、er_string(char str);extern void detele_string(char str,char ch);extern void print_string(char str);*以上3行声明在 本函数中将要调用的在其他文件中定义的 3个函数*char c;char str80; scanf(%c,&c); detele_string(str,c); print_string(str);fiiilllleeee(文件)#i#i#i#innnnccccllluuuuddddeeee voivoivoiddd eeeennnntttteeeerrrr_s_s_s_sttttrr

293、rriiinnnng(g(cccchhhharararar ssssttttrrrr8080)* 定义外部函数eeeennnntttteeeerrrr-ssssttttrrrriiinnnngggg*gegegegettttssss(ssssttttrrrr);*向字符数组输入字符串*fiiilllleeee(文件)voivoivoiddd ddddeeeellleeeetttteeee_s_s_s_sttttrrrriinnnng(g(cccchhhharararar ssssttttrrrr,c,c,c,chhhharararar cccchhhh)*定义外部函数ddddeeeelllee

294、eetttteeee_s_s_s_sttttrrrriiinnnngggg *iinnnntttt iii,j,j,j,j; forororor(ii=jjjj=0;0;0;0;ssssttttrrrrii!=0;iii+)iif(ssssttttrrrrii!=cccchhhh) ssssttttrrrrjjjj+=ssssttttrrrrii;ssssttttrrrrii=0;file(文件)#include void enter_string(char str80)* 定义外部函数enter-string*gets(str);*向字符数组输入字符串*file(文件)void delete

295、_string(char str,char ch)*定义外部函数delete_string *int i,j; for(i=j=0;stri!=0;i+)if(stri!=ch)strj+=stri;stri=0;ffiilleeee(文件)#iinnnnccccllluuuuddddeeee vvvvooooiiiidddd pppprrrriinnnntttt_ssssttttrrrriinnnngggg(cccchhhhaaaarrrr ssssttttrrrr)pppprrrriiiinnnnttttf(%s%s%s%snnnn,ssssttttrrrr);file(文件)#inclu

296、de void print_string(char str)printf(%sn,str);运行情况如下: (输入)(输入要删去的字符)(输出已删去指定字符的字符串)第九章本章要点预处理的概念C语言处理系统的预处理功能预处理命令的使用主要内容9.1宏定义9.2“文件包含”处理9.3条件编译基本概念ANSI C标准规定可以在源程序中加入一些“ 预处理命令”,以改进程序设计环境,提高编 程效率。这些预处理命令是由ANSI C统一规定的,但是 它不是C语言本身的组成部分,不能直接对它 们进行编译(因为编译程序不能识别它们)。 必须在对程序进行通常的编译之前,先对程序 中这些特殊的命令进行“预处理”

297、经过预处理后程序可由编译程序对预处理后的 源程序进行通常的编译处理,得到可供执行的 目标代码。基本概念C语言与其他高级语言的一个重要区别是可以 使用预处理命令和具有预处理的功能。提供的预处理功能主要有以下三种:宏定义文件包含条件编译 这些功能分别用宏定义命令、文件包含命令、条件编译命令来实现。为了与一般语句 相区别,这些命令以符号“”开头。例如:#define#include9.1 宏定义define标识符字符串例如:definePI3.14159269.1.1 不带参数的宏定义 宏定义一般形式为:宏定义的作用是在本程序文件中用指定的标识符PI 来代替“3.1415926”这个字符串,在编译预

298、处理时, 将程序中在该命令以后出现的所有的PI都用 “3.1415926”代替。这种方法使用户能以一个简单的 名字代替一个长的字符串.这个标识符(名字)称为“宏名”在预编译时将宏名替换成字符串的过程称为“宏展开”。define是宏定义命令。例9.1使用不带参数的宏定义#include #definePI3.1415926 void main()float l,s,r,v; printf(input radius:); scanf(%f,&r); l=2.0*PI*r;s=PI*r*r; v=4.0/3*PI*r*r*r;printf(l=%10.4fns=%10.4fnv=%10.4fn,l,

299、s,v);1=25.1328 s=50.2655 v=150.7966运行情况如下:input radius: 4说明:(1) 宏名一般习惯用大写字母表示,以便与变量名相区别。但这并非规定,也可用小写字母。(2) 使用宏名代替一个字符串,可以减少程序中重 复书写某些字符串的工作量。(3) 宏定义是用宏名代替一个字符串,只作简单置 换,不作正确性检查。只有在编译已被宏展开后 的源程序时才会发现语法错误并报错。(1) 宏名一般习惯用大写字母表示,以便与变量名 相区别。但这并非规定,也可用小写字母。(2) 使用宏名代替一个字符串,可以减少程序中重 复书写某些字符串的工作量。(3) 宏定义是用宏名代替

300、一个字符串,只作简单置 换,不作正确性检查。只有在编译已被宏展开后 的源程序时才会发现语法错误并报错。说明:(4) 宏定义不是语句,不必在行末加分号。如果加了分号则会连分号一起进行置换。(5) define命令出现在程序中函数的外面,宏名 的有效范围为定义命令之后到本源文件结束。通 常,define命令写在文件开头,函数之前,作为文件一部分,在此文件范围内有效。(6) 可以用undef命令终止宏定义的作用域。 例如:(4) 宏定义不是语句,不必在行末加分号。如果 加了分号则会连分号一起进行置换。(5) define命令出现在程序中函数的外面,宏名的有效范围为定义命令之后到本源文件结束。通 常,

301、define命令写在文件开头,函数之前,作 为文件一部分,在此文件范围内有效。(6) 可以用undef命令终止宏定义的作用域。 例如:#define G9.8void main()G的有效范围-在f1函数中,不再代表 9.8。这样可以灵活控制宏 定义的作用范围。#undef G f1()(7) 在进行宏定义时,可以引用已定义的宏名,可以层层置换。说明:(7) 在进行宏定义时,可以引用已定义的宏名,可 以层层置换。例9.2在宏定义中引用已定义的宏名#include #define R 3.0#define PI 3.1415926#define L 2*PI*R#define S PI*R*R

302、voidmain()printf(L=%fnS=%fn,L,S);运行情况如下:L=18.849556 S=28.274333经过宏展开后,printf函数中的输出项被展开 为:2*3.1415926*3.0展开为3.1415926*3.0*3.0printf函数调用语句展开为:printf(“L=%FNS=%fn”, 2*3.1415926*3.0,3.1415926*3.0*3.0);(8) 对程序中用双撇号括起来的字符串内的字符,即使与宏名相同,也不进行置换。(9) 宏定义是专门用于预处理命令的一个专用名词,它与定义变量的含义不同,只作字符替换,不 分配内存空间。说明:(8) 对程序中用

303、双撇号括起来的字符串内的字符, 即使与宏名相同,也不进行置换。(9) 宏定义是专门用于预处理命令的一个专用名词,它与定义变量的含义不同,只作字符替换,不 分配内存空间。9.1.2 带参数的宏定义作用:不是进行简单的字符串替换,还 要进行参数替换。 带参数的宏定义一般形式为:define宏名(参数表)字符串字符串中包含在括弧中所指定的参数程序中用和分别代替宏 定义中的形式参数和b, 用*代替S(3,2) 。因此 赋值语句展开为:area=3*2例:#defineS(a,b) a*b:area=S(3,2);对带参的宏定义是这样展开置换的:对带实参的宏(如S(3, 2),则按define命令 行中

304、指定的字符串从左到 右进行置换。若串中包含 宏中的形参(如a、b), 则将程序中相应的实参( 可以是常量、变量或表达 式)代替形参。如果宏定 义中的字符串中的字符不 是参数字符(如*中 的*号),则保留。这样 就形成了置换的字符串。例9.3使用带参的宏#include #define PI 3.1415926运行情况如下:=3.600000 area=40.715038#define S(r) PI*r*rvoid main()float a,area; a=3.6;area=S(a); printf(r=%fnarea=%fn,a,area);赋值语句“area=S(a); ” 经宏展开后为

305、: area=3.1415926*;(1)对带参数的宏展开只是将语句中的宏名后面 括号内的实参字符串代替define 命令行 中的形参。(2) 在宏定义时,在宏名与带参数的括弧之间不 应加空格,否则将空格以后的字符都作为 替代字符串的一部分。说明:(1)对带参数的宏展开只是将语句中的宏名后面 括号内的实参字符串代替define 命令行 中的形参。(2) 在宏定义时,在宏名与带参数的括弧之间不 应加空格,否则将空格以后的字符都作为 替代字符串的一部分。(1) 函数调用时,先求出实参表达式的值,然后代入形参。而使用带参的宏只是进行简单的字符替换。(2) 函数调用是在程序运行时处理的,为形参分配临时

306、 的内存单元。而宏展开则是在编译前进行的,在展 开时并不分配内存单元,不进行值的传递处理,也 没有“返回值”的概念。(3) 对函数中的实参和形参类型要求一致。而宏名无类 型,它的参数也无类型,只是一个符号代表,展开 时代入指定的字符串即可。宏定义时,字符串可以 是任何类型的数据。(4) 调用函数只可得到一个返回值,而用宏可以设法得 到几个结果。带参数的宏和函数的区别:(1) 函数调用时,先求出实参表达式的值,然后代入形 参。而使用带参的宏只是进行简单的字符替换。(2) 函数调用是在程序运行时处理的,为形参分配临时 的内存单元。而宏展开则是在编译前进行的,在展 开时并不分配内存单元,不进行值的传

307、递处理,也 没有“返回值”的概念。(3) 对函数中的实参和形参类型要求一致。而宏名无类 型,它的参数也无类型,只是一个符号代表,展开 时代入指定的字符串即可。宏定义时,字符串可以 是任何类型的数据。(4) 调用函数只可得到一个返回值,而用宏可以设法得 到几个结果。例9.4通过宏展开得到若干个结果#include#definePI3.1415926#defineCIRCLE(R,L,S,V) L=2*PI*R;S=PI*R*R;V=4.0/3.0*PI*R*R*Rvoidmain()floatr,l,s,v;scanf(%f,&r);CIRCLE(r,l,s,v);printf(r=%6.2f,

308、l=%6.2f,s=%6.2f,v=%6.2fn, r,l,s,v);对宏进行预编译,展开后的main函数如下:voidmain()floatr,l,s,v;scanf(%f,&r); l=2*3.1415926*r;s=3.1515926*r*r; v=4.0/3/0*3.1415926*r*r*r;printf(”r=%6.2f,l=%6.2f,s=%6.2f,v=%6.2fn”,r,l,s,v);3.5 r=3.50,l=21.99,s=38.48,v=179.59运行情况如下:带参数的宏和函数的区别:(5) 使用宏次数多时,宏展开后源程序长,因为每 展开一次都使程序增长,而函数调用不会

309、使源 程序变长。(6) 宏替换不占运行时间,只占编译时间。而函数 调用则占运行时间(分配单元、保留现场、值 传递、返回)。(5) 使用宏次数多时,宏展开后源程序长,因为每展开一次都使程序增长,而函数调用不会使源 程序变长。(6) 宏替换不占运行时间,只占编译时间。而函数 调用则占运行时间(分配单元、保留现场、值 传递、返回)。如果善于利用宏定义,可以实现程序的 简化,如事先将程序中的“输出格式”定 义好,以减少在输出语句中每次都要写出具体的输出格式的麻烦。例9.5通过宏展开得到若干个结果#include#definePRprintf#defineNLn#defineD%d#defineD1DN

310、L#defineD2DDNL#defineD3DDDNL#defineD4DDDDNL#defineS%svoidmain()inta,b,c,d;charstring=CHINA; a=1;b=2;c=3;d=4; PR(D1,a);PR(D2,a,b);PR(D3,a,b,c);PR(D4,a,b,c,d);PR(S,string);运行时输出结果:9.2 “文件包含”处理所谓“文件包含”处理是指一个源文件可以将另外 一个源文件的全部内容包含进来。语言提供了#include命令用来实现“文件包含”的操作。其一般形式为:#include文件名或#include(2)主文件file1.c#in

311、cludeincludeformat.h voidmain()inta,b,c,d;charstring=CHINA; a=1;b=2;c=3;d=4; PR(D1,a);PR(D2,a,b);PR(D3,a,b,c);PR(D4,a,b,c,d);PR(S,string);例9.6将例95时格式宏做成头文件,把它 包含在用户程序中。(1)将格式宏做成头文件format.h#include#definePRprintf#defineNLn#defineD%d#defineD1DNL#defineD2DDNL#defineD3DDDNL#defineD4DDDDNL#defineS%s注意:在编

312、译时并不是分别对两个文件分别进行编 译,然后再将它们的目标程序连接的,而是 在经过编译预处理后将头文件format.h包含 到主文件中,得到一个新的源程序,然后对 这个文件进行编译,得到一个目标(.obj) 文件。被包含的文件成为新的源文件的一部 分,而单独生成目标文件。说明:(1) 一个#include命令只能指定一个被包含文件,如果要包含个文件,要用个#include命令。(2) 如果文件包含文件,而在文件中要用到 文件的内容,则可在文件中用两个include命 令分别包含文件和文件,而且文件应出现 在文件之前,即在file1.c中定义。(3) 在一个被包含文件中又可以包含另一个被包含 文

313、件,即文件包含是可以嵌套的。(1) 一个#include命令只能指定一个被包含文件, 如果要包含个文件,要用个#include命令。(2) 如果文件包含文件,而在文件中要用到 文件的内容,则可在文件中用两个include命 令分别包含文件和文件,而且文件应出现 在文件之前,即在file1.c中定义。的。(3) 在一个被包含文件中又可以包含另一个被包含 文件,即文件包含是可以嵌套说明:(4) 在#include命令中,文件名可以用双撇号或尖括号括起来。(5) 被包含文件(file2.h)与其所在的文件(即用#include命令的源文件file2.c),在预编译后已 成为同一个文件(而不是两个文件

314、)。因此,如 果file2.h中有全局静态变量,它也在file1.h文 件中有效,不必用extern声明。(4) 在#include命令中,文件名可以用双撇号或尖 括号括起来。(5) 被包含文件(file2.h)与其所在的文件(即用#include命令的源文件file2.c),在预编译后已 成为同一个文件(而不是两个文件)。因此,如 果file2.h中有全局静态变量,它也在file1.h文 件中有效,不必用extern声明。9. 条件编译概念:所谓“条件编译” 定编译的条件,使 才进行编译。条件编译命令的几种形(3) if 表达式,是对部分内容指 其只在满足一定条件式:程序段else程序段en

315、dif(1)ifdef 标识符程序段else程序段endif(2)ifndef 标识符程序段else程序段endif#include#defineLETTER1 voidmain()据需要设置条件输出,或全改为charstr20=CLanguage,c;inti; i=0;while(c=stri)!=0)i+;#ifLETTERif(c=a&c=A&c=Z) c=c+32;#endif printf(%c,c);第十章10.1地址和指针的概念为了说清楚什么是指针,必须弄清楚数据在内存中是 如何存储的,又是如何读取的。内存区的每一个字节有一个编号,这就是“地址” 。如果在程序中定义了一个变量,

316、在对程序进行编译 时,系统就会给这个变量分配内存单元。、按变量地址存取变量值的方式称为“直接访问”方式例如:(,); (,);另一种存取变量值的方式称为“间接访问”的方式。即,将变量的地址存放在另一个变量中。在语言中,指针是一种特殊的变量,它是存放地址的。假设我们定义了一个指针变量i_pointer用来存放整型变 量的地址,它被分配地址为(3010)、(3011)的两个字节。可以通过语句:i_pointer ;将的地址(2000)存放到i_pointer中。这时, i_pointer 的值就是(2000) ,即变量所占用单元的起始地址。要 存取变量的值,可以采用间接方式:先找到存放“ 的地址”

317、的变量i_pointer ,从中取出的地址(2000), 然后到2000 、 200字节取出的值()。指针和指针变量的定义:一个变量的地址称为该变量的“指针”。例如,地址2000是变量的指针。如果有一个变量专 门用来存放另一变量的地址(即指针),则它称为“指针变量”。上述的i_pointer就是一个指针变量。指针变量的值(即指针变量中存放的值)是地 址(即指针)。请区分“指针”和“指针变量”这 两个概念。10.2 变量的指针和指向变量的指针变量10.1 定义一个指针变量定义指针变量的一般形式为 基类型*指针变量名;下面都是合法的定义:float*pointer_;/ pointer_是指向fl

318、oat型变量的 指针变量char *pointer_; /pointer_是指向字符型变量的指针变量 可以用赋值语句使一个 指针变量得到另一个变 量的地址,从而使它指 向一个该变量。如:pointer_; pointer_;在定义指针变量时要注意两点:指针变量前面的“*”,表示该变量的类型为指针型变 量。例: float*pointer_1;指针变量名是pointer_1 ,而不是* pointer_1 。(2) 在定义指针变量时必须指定基类型。 需要特别注意的是,只有整型变量的地址才能放到指向整型变量的指针变量中。下面的赋值是错误的floata;int* pointer_1;pointer_

319、1=&a;/* 将float型变量的地址放到指 向整型变量的指针变量中,错误 */10. 指针变量的引用请牢记,指针变量中只能存放地址(指针), 不要将一个整数(或任何其他非地址类型的数据) 赋给一个指针变量。例10. 通过指针变量访问整型变量#include voidmain ( ) int ,;int*pointer_, *pointer_; ; pointer_; /*把变量的地址赋给pointer_1 */pointer_; /*把变量的地址赋给pointer_ */ printf(%,%,); printf(%,%,*pointer_, *pointer_);对“”和“*”运算符说明

320、:如果已执行了语句pointer_;(1)* pointer_的含义是什么?“”和“*”两个运算 符的优先级别相同,但按自右而左方向结合,因此 先进行* pointer_的运算,它就是变量,再执行运算。因此,* pointer_与相同,即变量a 的地址。如果有pointer_2 * pointer_ ; 它的作用是将(的地址)赋给pointer_2 ,如 果pointer_2原来指向,经过重新赋值后它已不再 指向了,而指向了。(2) *的含义是什么?先进行运算,得的地址,再进行*运算。即所指向的变量,也就是变量 a。*和*pointer_的作用是一样的,它们都等价 于变量。即*与等价。(3)

321、(*pointer_)相当于。注意括号是必 要的,如果没有括号,就成为了*pointer_,从 附录可知:+和*为同一优先级别,而结合方向为自 右而左,因此它相当于*(pointer_)。由于+在 pointer_1的右侧,是“后加”,因此先对pointer_的原 值进行*运算,得到的值,然后使pointer_的值改 变,这样pointer_不再指向了。例10 . 2输入和两个整数,按先大后小的顺序输出 和。#include voidmain() int *1,*2,*,;scanf(,); 1;if(); printf(=,=,); printf(max=,min=,*1,*2);运行情况如

322、下: , , 当输入,时,由于, 将和交换。交换前的情况见图(),交换后见图()。10.3 指针变量作为函数参数例10 . 3对输入的两个整数按大小顺序输出#include void main()void swap(int *,int *);int ,;int *pointer_,*pointer_; scanf(,); pointer_ ; pointer_2 ;if(swap( pointer_ , pointer_2 );printf(,);void swap(int *,int *)inttemp; temp*1;*;*temp;例10. 输入、 3个整数,按大小顺序输出#includ

323、e void main() voidexchange(int *1, int *2, int *3);int,*,*,*; scanf(%,%,%,&, &, &); ; exchange (,); printf(,);voidexchange(int *, int *, int *) voidswap(int *, int *); if(*) swap(,);if(*) swap(,); if(* swap(,);voidswap(int *, int *)int temp; temp*;*;*temp;10.3 数组与指针一个变量有地址,一个数组包含若干元素,每个 数组元素都在内存中占用存

324、储单元,它们都有相 应的地址。指针变量既然可以指向变量,当然也可以指向数组元素(把某一元素的地址放到一个 指针变量中)。所谓数组元素的指针就是数组元 素的地址。10.3.1指向数组元素的指针定义一个指向数组元素的指针变量的方法,与以前介绍的指向变量的指针变量相同。例如: ; (定义为包含个整型数据的数组) *; (定义为指向整型变量的指针变量)应当注意,如果数组为型,则指针变量的 基类型亦应为型。对该指针变量赋值: ;把元素的地址赋给指针变量。也就是使 指向数组的第号元素,如图:10.通过指针引用数组元素引用一个数组元素,可以用:() 下标法,如形式;() 指针法,如*()或*()。其中是数组

325、名,是指向数组元素的指针变量,其初 值。例10.5输出数组中的全部元素假设有一个数组,整型,有个元素。要输出 各元素的值有三种方法:(1)下标法#include voidmain()int ;int;for(;) scanf(,); printf();for(;) printf(,);(2) 通过数组名计算数组元素地址,找出元素的值。#include voidmain()int ;int ;for(; ) scanf(,); printf();for(;) printf(,*();(3) 用指针变量指向数组元素。#include voidmain() int ;int*,; for(;)sca

326、nf(,); printf(); for(;();)printf( ,*);例10. 通过指针变量输出数组的个元素。有人编写出以下程序:#include voidmain() int*,; for(; ) scanf(,); printf();for(;, )printf(,*);这个程序乍看起来好像没有什么问题。有的人即使已被告知此程序有问题,还是找不出它有什么问题。我 们先看一下运行情况:1 2 3 4 5 6 7 8 9 022153 234 0 0 30036 25202 11631 8259 8237 28483显然输出的数值并不是数组中各元素的值解决这个问题的办法,只要在第二个循环

327、之前 加一个赋值语句:;#include voidmain() int*,; for(;) scanf(,); printg();p=a; for(;, ) printf(,*);10.3用数组名作函数参数在第8章8.7节中介绍过可以用数组名作函数的参数 如: void main()i(int ,int ); int ; (,);void (int ,int )f (int arr , int n) 但在编译时是将arr按指针变量处理的,相当于将函 数f的首部写成f (int *arr, int n)以上两种写法是等价的。需要说明的是:C语言调用函数时虚实结合的方法都 是采用“值传递”方式,当

328、用变量名作为函数参数时传 递的是变量的值,当用数组名作为函数参数时,由于 数组名代表的是数组首元素地址,因此传递的值是地 址,所以要求形参为指针变量。例10 将数组中个整数按相反顺序存放#include voidmain() voidinv(int ,int );int ,; printf(The original array:); for(;)printf (,);printf();inv (,);printf(The array has been in verted:); for(;)printf (,);printf ();voidinv(int ,int )/*形参x是数组名*/int

329、temp,();for(;);temp; ; temp;return;运行情况如下:The original array: , The array has beeninverted: ,对这个程序可以作一些改动。将函 数inv中的形参改成指针变量。#include voidmain()voidinv(int *,int );int ,;printf( The original array: );for(;) printf (,); printf ();inv(,);printf ( The array has been in verted: );for(;) printf (,);printf

330、 ();voidinv(int *,int )/*形参x为指针变量*/int,temp,*,*,(); ; for(;,)emp*;*;*temp;return;归纳起来,如果有一个实参数组,想在函数中改变此数组中的元素的值,实参与形参 的对应关系有以下种情况:(1) 形参和实参都用数组名,如:voidmain() int ;void(int ,int )(,);(2) 实参用数组名,形参用指针变量。如:void ()void (int *,int )int;(,);(3)实参形参都用指针变量。例如:voidmain()void (int *,int )int , *p=a;(p,); (4)

331、 实参为指针变量,形参为数组名。如:void main()void (int x ,int ) ,*p=a;(p,);#include voidmain()voidinv(int *,int );int ,*; printf(The original array:n ); for(;,) scanf(,);printf(); ;inv(,);/* 实参为指针变量 */printf(The array has been inverted :); for(; ) printf(,*);printf();voidinv(int *,int )int,temp,*,*; ();for(;,)emp*;

332、*;*temp;return;例109 用选择法对个整数按由大到小顺序排序#include voidmain() voidsort(int ,int ); int*,10;for(;) scanf(,);sort(,); for(,;)(,*);voidsort(int ,int ) int ,;for(;); for(;) (); (!); ;10. 多维数组与指针用指针变量可以指向一维数组中的元素,也可以指向 多维数组中的元素。但在概念上和使用上,多维数组 的指针比一维数组的指针要复杂一些。1. 多维数组元素的地址 先回顾一下多维数组的性质,可以认为二维数组是“数 组的数组”,例 :定义i

333、nt a34=1,3,5,7,; 则二维数组a是由3个一维数组所组成的。设二维数组 的首行的首地址为,则表 示 形 式含义地 址a二维数组名,指向一维数组 a0,即0行首地址2000a0,*(a+0),*a0行0列元素地址2000a+1,&a11行首地址2008a1,*(a+1)1行0列元素a10的地址2008A1+2,*(a+1)+2,&a121行2列元素a12 的地址2012*(a1+2),*(*(a+1)+2),a121行2列元素a12的值元素值为13例10.0 输出二维数组有关的值#include defineFROMAT, void main() int 341,3,5,7,9,;

334、printf(,*); printf(,0 , *(); printf(,0,00); printf(,1,); printf(,10,*(+)+); printf(,*(); printf(,); printf(,*(*();某一次运行结果如下:,(0行首地址和0行0列元素地址),(0行0列元素地址),(0行0首地址和0行0列元素地址),(1行0列元素地址和1行首地址),(1行0列元素地址),(2行0列元素地址),(2行首地址),(1行0列元素的值)2 . 指向多维数组元素的指针变量在了解上面的概念后,可以用指针变量指向多维数 组的元素。(1) 指向数组元素的指针变量运行结果如下:13579

335、111315192123例10.11 用指针变量输出二维数组元素的值#include void main() int 341,3,5,7,9,11,13,15,int*;17,19,21,23;for(;)() printf(); printf(,*); 可将程序最后两个语句改为printf(addr, value2,*); 在TC+环境下某一次运行时输出如下: , , , , , , , , , , ,(2) 指向由个元素组成的一维数组的指针变量例10.13 出二维数组任一行任一列元素的值#include 运行情况如下:voidmain () ,(本行为键盘输入 int 1,3,5,7,9,

336、11,13,15,; int (*),;scanf( ,);,*(*();运行情况如下: ,(本行为键盘输入) ,printf(,3. 用指向数组的指针作函数参数例10.13 有一个班,个学生,各学门课,计算总 平均分数以及第个学生的成绩。这个题目是很简单 的。只是为了说明用指向数组的指针作函数参数而举 的例子。用函数求总平均成绩,用函 数找出并输出第个学生的成绩。#include voidmain()void average(float *p,int n);void search(float (*p)4,int n);float score34=65,67,70,60,80,87,90,81

337、,90,99,100,98;average(*score,12);*求12个分数的平均分* search(score,);*求序号为的学生的成绩*voidaverage(float *,int ) float*_;float ,; _; (;_;) (*); ; printf(average,aver);voidsearch(float (*)4,int )/ * p是指向具有4个元素的一维数组的指针 */int ;printf(the score ofNo. % are:,); for(;) printf(5.2,*(*(); 程序运行结果如下: : .例10.4 在上题基础上,查找有一门以

338、上课程不及格的 学生,打印出他们的全部课程的成绩。#include void main()voidsearch(float (*p)4,int n);/*函数声明*/ floatscore34=65,57,70,60,58,87,90,81,90,99,100,98;search(score,);voidsearch(float (*p)4,int )int ,;for(;)flag; printf(No.%d fails,his scores are:n,j+1); for(;) printf(%.,*(*(); printf();for(;) if(*(*())flag; if()程序运行

339、结果如下: s, : . . . . s, . . . .10. 字符串与指针10字符串的表示形式(1) 用字符数组存放一个字符串,然后输出该字符串。例 10.5定义一个字符数组,对它初始化,然后输出该字符串#include voidmain()char string !;printf(,);(2) 用字符指针指向一个字符串可以不定义字符数组,而定义一个字符指针。用字符 指针指向字符串中的字符。例106 定义字符指针#include voidmain()charstring !;printf(,);例10.7 将字符串复制为字符串对字符串中字符的存取,可以用下标方法,也可以 用指针方法#inc

340、lude void ()char amaboy,20;int ;for(;*()!;)*()*();*(); printf(stringais :,); printf( :);for(;!;)printf(,); printf();也可以设指针变量,用它的值的改变来指向字符串 中的不同的字符。例10.8 用指针变量来处理例107问题。#include voidmain()char =I am a boy. ,20,*p1,*p2;int ; ;for(;*!;p1,p2)*;*;printf(string is:,); printf( :); for(;!;) printf(,); print

341、f();程序必须保证使和同步移动。10. 字符指针作函数参数例1019 用函数调用实现字符串的复制(1)用字符数组作参数#include voidmain() voidcopy_string(char from , char to );char a = am a teacher; char =you are a student;printf(“string a= string ,);printf(copy string a to string b:n ); copy_string (,);printf(nstring a=%snstring b=%sn,a,b);voidcopy_string

342、(char from , char to ) int ;while(!); ;程序运行结果如下: copy string a to string b: () 形参用字符指针变量#include voidmain() void copy_string(char *, char *);char * amateacher .;char *you are a student ;printf(string a= ,);printf(copy string a to string b:n ); _(,); printf(nstring a=%snstring b=%sn,a,b);voidcopy_str

343、ing(char *,char *) for(;*from!;from,to)*;*;() 对 copy_string 函数还可作简化1、将copy_string函数改写为voidcopy_string (char *,char *)while(*)!);2、 copy_string函数的函数体还可改为while(*to*from)!);3、copy_string函数的函数体还可写成while(*!)*;*;4、上面的while语句还可以进一步简化为下面的while语句: while(*); 它与下面语句等价: while(*to*from)!);将*赋给*,如果赋值后的*值等于,则循环终止(

344、已赋给*)5、函数体中语句也可以改用语句: for(;(*to*from)!;);或for(;*to*from;);6、也可用指针变量,函数copy_string可写为voidcopy_string (char from ,char )*,*; ; while(*p2*p1)!);10. 对使用字符指针变量和字符数组的讨论虽然用字符数组和字符指针变量都能实现字符串的 存储和运算,但它们二者之间是有区别的,不应混 为一谈,主要有以下几点:(1) 字符数组由若干个元素组成,每个元素中放一个字符,而字符指针变量中存放的是地址(字符串第1个字符的地址),决不是将字符串放到字符指针变 量中。(2)赋值方

345、式。对字符数组只能对各个元素赋值,不 能用以下办法对字符数组赋值。charstr;str !; 而对字符指针变量,可以采用下面方法赋值:char*; !; 但注意赋给的不是字符,而是字符串第一个元素 的地址。(3)对字符指针变量赋初值:char * love China!;等价于 char*; !; 而对数组的初始化:charstr loveChina!;不能等价于char str;str !;(4) 如果定义了一个字符数组,在编译时为它分配内存单元,它有确定的地址。而定义一个字符指针变 量时,给指针变量分配内存单元,在其中可以放一 个字符变量的地址也就是说,该指针变量可以指向 一个字符型数据

346、,但如果未对它赋予一个地址值, 则它并未具体指向一个确定的字符数据。如:char str; scanf(,str);是可以的。而常有人用下面的方法,目的是想输入一个字符串,虽 然一般也能运行,但这种方法是危险的 :char*; scanf(,);应当这样:*,; (,);(5) 指针变量的值是可以改变的,如:例10.0改变指针变量的值#include void ()char*I love China!;printf(,);需要说明,若定义了一个指针变量,并使它指向一个字符串,就可以用下标形式引用指针变量所指的字符 串中的字符。例101#include voidmain()char* love

347、hina!;int ;printf ( “ The sixth character is %cn,a5); for(;!;) printf(,);10. 指向函数的指针10. 用函数指针变量调用函数可以用指针变量指向整型变量、字符串、数组,也可以指向一个函数。一个函数在编译时被分配 给一个入口地址。这个函数的入口地址就称为函 数的指针。例10.2求和中的大者。先列出按一般方法的程序。#include voidmain() intmax(int,int);int ,; scanf(,); (,); printf(,);intmax(int ,int ) int ;if(); else;retur

348、n();将 main 函数改写为#include voidmain() int(int,int);int (*)(); int ,; ;scanf(,); (*)(,); printf(,);10. 用指向函数的指针作函数参数函数指针变量常用的用途之一是把指针作为参数传递 到其他函数。前面介绍过,函数的参数可以是变量、 指向变量的指针变量、数组名、指向数组的指针变量 等。现在介绍指向函数的指针也可以作为参数,以实 现函数地址的传递,这样就能够在被调用的函数中使 用实参函数。它的原理可以简述如下:有一个函数(假设函数名为sub),它有两个形参(x1和x2), 定义x1和x2为指向函数的指针变量。

349、在调用函数sub 时,实参为两个函数名和,给形参传递的是 函数和的地址。这样在函数中就可以 调用和函数了。实参函数名f1void (int (*x1)(int),int (*x2)(int,int)) int ,; (*)(); *调用函数*(*)(,);*调用函数*例10.3 设一个函数process,在调用它的时候,每次 实现不同的功能。输入和两个数,第一次调用 process时找出和中大者,第二次找出其中小者, 第三次求与之和。#include void main() int max(int,int);/* 函数声明 */int min(int,int);/* 函数声明 */ int a

350、dd(int,int);/* 函数声明 */void process (int,int,int(*fun)();/* 函数声明 */int ,;printf( :);scanf(,);(); (,); (); (,); (); (,);int(int ,int )/* 函数定义 */ ; (); ; ();int(int ,int )/* 函数定义 */ ; (); ;();int(int ,int )/* 函数定义 */ ; ; ();voidprocess(int ,int ,int (*fun)(int,int) ; (*)(,); (,);10. 返回指针值的函数一个函数可以带回一个整

351、型值、字符值、实型值等, 也可以带回指针型的数据,即地址。其概念与以前类 似,只是带回的值的类型是指针类型而已。 这种带回指针值的函数,一般定义形式为类型名 *函数名(参数表列);例如:*(int ,int );例104 有若干个学生的成绩(每个学生有门课 程),要求在用户输入学生序号以后,能输出该学生 的全部成绩。用指针函数来实现。#include voidmain()float *score 4=60,70,80,90,56,89,67,88,34,78,90,66;float*search(float (*pointer)4,int n); float*;int ,;printf(ent

352、er the number ofstudent:); scanf(,);printf(The scores of No are:,);search(,); for(; printf(,*();float * search(float (*)4,int ) float *; *(); return(); 运行情况如下:enter the number ofstudent: The scores ofNo. are:56.0089.0067.0088.00例105 对上例中的学生,找出其中有不及格课 程的学生及其学生号。#include voidmain()floatscore 4=60,70,8

353、0,90,56,89,67,88,34,78,90,66;float search(float (*pointer)4); float*;i ,;f(;)(); (*()printf(scores:,); for(;)printf(,*(); printf();10.指针数组和指向指针的指针10. 指针数组的概念一个数组,若其元素均为指针类型数据,称为指针 数组,也就是说,指针数组中的每一个元素都相当于一个指针变量。一维指针数组的定义形式为 类型名数组名数组长度;例如:*;例106 将若干字符串按字母顺序(由小到大)输出。#include #include voidmain()voidsort

354、(char *name ,int n);voidprintf(char *name ,int n);char *name =Follow me,BASIC,Great Wall,FORTRAN,Computer design;int ;sort(,); print(,);voidsort(char * ,int )char*; int ,;for(;for(; if(strcmp(name,name)0)=; if(!)temp=namei; namei=namek; namek=temp;voidprint(char * ,int ) ; (;) printf(,);运行结果为: 10指向指

355、针的指针怎样定义一个指向指针数据的指针变量呢?如下: *; 的前面有两个*号。*运算符的结合性是从右到左,因此*相当于*(*),显然*是指针变 量的定义形式。如果没有最前面的*,那就是定义 了一个指向字符数据的指针变量。现在它前面又有 一个*号,表示指针变量是指向一个字符指针变 量的。*就是所指向的另一个指针变量。例107 使用指向指针的指针#include voidmain()char *name=Follow me,BASIC,GreatWall,FORTRAN,Computer design; char*;int ;for(;);printf(,*);例10.28 一个指针数组的元素指向

356、整型数据的简单例子#include void main()int ,;int *num5=&a0,&a1,&a2,&a3,&a4;int*,; ;for(; printf(,*); ;10. 指针数组作函数的形参指针数组的一个重要应用是作为main函数的形参。在 以往的程序中,函数的第一行一般写成以下 形式:voidmain() 括弧中是空的。实际上,main函数可以有参数,例如: voidmain(intargc,char*argv ) argc和argv就是main函数的形参。main函数是由操作系 统调用的。那么,main函数的形参的值从何处得到 呢?显然不可能在程序中得到。实际上实参是

357、和命令一起给出的。也就是在一个命令行中包括命令名和需 要传给main函数的参数。命令行的一般形式为命令名 参数 参数参数如果有一个名为的文件,它包含以下的 函数:voidmain(int ,char * )(); (,); 在DOS命令状态下输入的命令行为 则执行以上命令行将会输出以下信息: 10.有关指针的数据类型和指针运算的小结10.8.1有关指针的数据类型的小结定义含义 ;定义整型变量*;为指向整型数据的指针变量int an;定义整型数组,它有个元素*;定义指针数组,它由个指向整型数据的指针 元素组成 (*);为指向含个元素的一维数组的指针变量 ();为带回整型函数值的函数*();为带回

358、一个指针的函数,该指针指向整型数据 (*)();为指向函数的指针,该函数返回一个整型值 *;是一个指针变量,它指向一个指向整型数据的 指针变量10.8.2 指针运算小结(1) 指针变量加(减)一个整数 例如:、 等。(2) 指针变量赋值 将一个变量地址赋给一个指针变量。如: ; (将变量的地址赋给)array; (将数组首元素地址赋给) array;(将数组第个元素的地址赋给) max;(为已定义的函数,将的入口地址赋给);(和都是指针变量,将的值赋给)(3) 指针变量可以有空值,即该指针变量不指向任何变量,可以这样表示:;(4) 两个指针变量可以相减 如果两个指针变量都指向同一个数组中的元素

359、,则两 个指针变量值之差是两个指针之间的元素个数(5) 两个指针变量比较若两个指针指向同一个数组的元素,则可以进行比较。 指向前面的元素的指针变量“小于”指向后面元素的指 针变量。10.8.3 void指针类型ANSIC新标准增加了一种“void”指针类型,即可定义一 个指针变量,但不指定它是指向哪一种类型数据的。 ANSIC标准规定用动态存储分配函数时返回void指针, 它可以用来指向一个抽象的类型的数据,在将它的值 赋给另一指针变量时要进行强制类型转换使之适合于 被赋值的变量的类型。例如:*; *;( *);同样可以用( *)将的值转换成 *类型。如:( *);也可以将一个函数定义为 *类

360、型,如: *(char ch1,char ch2) 表示函数返回的是一个地址,它指向“空类 型”,如需要引用此地址,也需要根据情况对之进行 类型转换,如对该函数调用得到的地址要进行以下 转换:( *)(h,h);第十一章本章要点结构体的概念 结构体的定义和引用 结构体数组主要内容11.1概述11.2定义结构体类型变量的方法11.3结构体变量的引用11.4结构体变量的初始化11.5结构体数组11.指向结构体类型数据的指针11.7用指针处理链表11.8共用体11.9枚举类型11.10用typedef定义类型11.1概述问题定义:有时需要将不同类型的数据组合成一个有机 的整体,以便于引用。如: 一个

361、学生有学号/姓名/性别/年龄/地址等属性int age;int char addr30;应当把它们组织成一个组合项,在一个组合 项中包含若干个类型不同(当然也可以相同) 的数据项。 图11-1Numnamesexagescoreaddr图11-1int num; char name20;char sex;100101Li FunM1887.5Beijing11.1概述声明一个结构体类型的一般形式为:struct结构体名成员表列;如:struct student结构体名int num;char name20;char sex;int age;float score;char addr30;类型名

362、成员名11.2定义结构体类型变量的方法可以采取以下3种方法定义结构体类型变 量:(1)先声明结构体类型再定义变量名|结构体类型名结构体变量名 定义了student1和student2为struct student类型的变量,即它们具有struct student类型的结构.图11-2student1student2图11-2例如:struct studentstudent1, student2;100102WangLiF2098Beijing100101 ZhangXinM1990.5Shanghai11.2定义结构体类型变量的方法在定义了结构体变量后,系统会为之分配内 存单元。例如:stud

363、ent1和student2在内存中各占59个字节(2+20+1+2+4+30=59)。注意:注意:注意:注意:将一个变量定义为标准类型(基本数据类型)与 定义为结构体类型不同之处在于后者不仅要求指定 变量为结构体类型,而且要求指定为某一特定的结 构体类型,因为可以定义出许许多多种具体的结构 体类型。注意:将一个变量定义为标准类型(基本数据类型)与 定义为结构体类型不同之处在于后者不仅要求指定 变量为结构体类型,而且要求指定为某一特定的结 构体类型,因为可以定义出许许多多种具体的结构 体类型。11.2定义结构体类型变量的方法(2)在声明类型的同时定义变量 这种形式的定义的一般形式为:struct

364、结构体名成员表列变量名表列;11.2定义结构体类型变量的方法例如:struct studentint num;char name20; char sex;int age; float score;char addr30;student1,student2;它的作用与第一 种方法相同,即 定义了两个struct student 类型的变 量student1,student2型变量的方法注意:注意:注意:注意:变量(1) 类型与变量是不同的概念,不要混同。只 能对变量赋值、存取或 运算,而不能对一个类 型赋值、存取或运算。 在编译时,对类型是不 分配空间的,只对变量 分配空间。注意:注意:注意:注

365、意:注意:注意:注意:注意:注意:注意:注意:注意:(1)(2)对结构体中的成员(类型与变量是不同 的概念,不要混同。只即“域”),可以单独 能对变量赋值、存取或使用,它的作用与地位 运算,而不能对一个类相当于普通变量。型赋值、存取或运算。(3)成员也可以是一个结 在编译时,对类型是不构体变量。 分配空间的,只对变量(4)成员名可以与程序 分配空间。中的变量名相同,二者不代表同一对象。11.2定义结构体类(3) 直接定义结构体类型 其一般形式为:struct成员表列变量名表列; 即不出现结构体名。注意:(2)对结构体中的成员( 即“域”),可以单独 使用,它的作用与地位 相当于普通变量。(3)

366、成员也可以是一个结 构体变量。(4) 成员名可以与程序 中的变量名相同,二者不 代表同一对象。型变量的方法明一个结构体类型*/ay;date类型*/11.2定义结构体类例如:structdate/*声intnum;先声明一个struct date类型,它代表“日期”,包括3个成员:charname20; month(月)、day(日)、year(年)。charsex;floatscore;员bbbbiiiirrrrtttthhhhddddayayayay指定为 structdatebirthdssssttttrrrruuuucccctttt ddddatatatateeee类型。/*birth

367、day是structcharaddr30;student1,student2;图11-3intage;然后在声明struct student类型时,将成birthdayaddrNumnamesexageMonthdayyear图11-311.3结构体变量的引用在定义了结构体变量以后,当然可以 引用这个变量。但应遵守以下规则:(1)不能将一个结构体变量作为一个整体进行 输入和输出。例如: 已定义student1和student2为结构体变量并且它们已有值。printf(%d,%s,%c,%d,%f,%n,studrent1);11.3结构体变量的引用引用结构体变量中成员的方式为结构体变量名.成员

368、名例 如 , student1.num表示student1 变量中的 num成员,即student1的num(学号)项。可 以 对 变 量 的 成 员 赋 值 , 例 如:student1.num=10010;“.”是成员(分量)运算符,它在所有的运算符中优先级最高,因 此可以把student1.num作为一个整体来看 待。上面赋值语句的作用是将整数10010 赋给student1变量中的成员num。一个结构体类型注意:注意:注意:注意:,则要用 不能用级一级地找到最低 能对最低级的成员进行算。student1.birthday构体变量来访问student1,student1可变 量中的成员

369、birthday,因为onthbirthday本身是一 个结构体变量。11.3结构体变量的引用(2) 如果成员本身又属注意:若干个成员运算符,一不能用 的一级的成员。只赋值或存取以及运 例如:对上面定义的结以这样访问各成员:student1.num student1.birthday.mstudent1.birthday来访问student1变 量中的成员 birthday,因为 birthday本身是一 个结构体变量。可以像普通变量一样 其类型决定可以进行nt1.score; tudent2.score;11.3结构体变量的引用(3) 对结构体变量的成员进行各种运算(根据 的运算)。例如:s

370、tudent2.score=stude sum=student1.score+s student1.age+;+student2.age;由于“”运算符的优先级最高,因此 是对 进 行自加运算,而不是 先对进行自加 运算。11.3结构体变量的引用(4) 可以引用结构体变量成员的地址,也可以 引用结构体变量的地址。例如: scanf(%d,&student1.num);(输入student1.num的值) printf(%o,student1);(输出student1的首地址)11.3结构体变量的引用但不能用以下语句整体读入结构体变量, 例如:scanf(%d,s,c,d,f, s,studen

371、t1);结构体变量的地址主要用作函数参数, 传递结构体变量的地址。运行结果:例11.1对结构体变量No.:10101name:LiLin sex:address:123 Beijing Road#include voidmain()structstudentlongintnum;char name20; charsex;charaddr20;a=10101,LiLin,M,123Beijing Road;/* 对结构体变量a赋初值*/ printf(No.:%ldnname:%snsex:%cnaddress:%sn,a.num,a.name,a.sex,a.addr);但不能用以下语句整体读

372、入结构体变量, 例如:scanf(%d,s,c,d,f, s,student1);结构体变量的地址主要用作函数参数, 传递结构体变量的地址。的初始化初始化.11.结构体变量11.5 结构体数组一个结构体变量中可以存放一组数 据(如一个学生的学号、姓名、成绩等 数据)。如果有个学生的数据需要 参加运算,显然应该用数组,这就是结 构体数组。结构体数组与以前介绍过的 数值型数组不同之处在于每个数组元素 都是一个结构体类型的数据,它们都分 别包括各个成员(分量)项。11.5 结构体数组11.5.1定义结构体数组和定义结构体变量的方法相仿,只需说明 其为数组即可。例如:struct studentar

373、addr30;int num;char name20;char sex;int age;float score;ch;struct student3;以上定义了一个数 组stu,数组有个元 素,均为struct student类型数据。11.5 结构体数组也可以直接定义一个结构体数组,例如: structstudent:strcutstudentintnum;stu3;图11-4或intnum;stu3;图11-4初始化对结构体数组可以; char sex;; char addr30;,LiLin, 10310102,Zhang 9,130图11-511.5 结构体数组11.5.2结构体数组的

374、与其他类型的数组一样, 初始化。例如:structstudentint num;char name20 int age; float score;stu210101M,18,87.5, BeijingRoad, Fun,M,19,9 Shanghai Road;图11-511.5 结构体数组当然,数组的初始化也可以用以下形式:struct studentint num;结构体数组初始化的一般形 式是在定义数组的后面加上 “初值表列;”。structstudentstr,;即先声明结构体类型,然后定义数组为该 结构体类型,在定义数组时初始化。11.5 结构体数组11.5.3结构体数组应用举例例1

375、1.2对候选人得票的统计程序。设有3个候选人,每次 输入一个得票的候选人的名字,要求最后输出各人得票 结果。#include #include struct personchar name20;in count;leader3=“Li”,0,”Zhang”,0,”Fun”,0e20;e); e,leaderj.name)=0i.name,leaderi.co例11.2void main() int i,j;char leader_nam for(i=1;i=10;i+)scanf(“%s”,leader_nam for(j=0;j3;j+) if(strcmp(leader_namleader

376、j.count+;printf(“n”); for(i=0;i3;i+)printf(“%5s:%dn”,leadernt);运行结果: : :)u11.5 结构体数组在主函数中定义字符数组leader-name,它 代表被选人的姓名,在10次循环中每次 先输入一个被选人的具体人名,然后把 它与3个候选人姓名相比,看它和哪一个 候选人的名字相同。在输入和统计结束图11-6之后,将3人的名字和得票数输出。程序定义一个全局的结构体数组leader,它 有个元素,每一个元素包含两个成员 name(姓名)和count(票数)。在定义 数组时使之初始化,使3位候选人的票数nameLi ZhangFunc

377、ount000图11-6都先置零.11.6 指向结构体类型数据的指针一个结构体变量的指针就是该变量所占据 的内存段的起始地址。可以设一个指针 变量,用来指向一个结构体变量,此时 该指针变量的值是结构体变量的起始地 址。指针变量也可以用来指向结构体数 组中的元素.11.6.1指向结构体变量的指针下面通过一个简单例子来说明指向结构体变 量的指针变量的应用。用ore;,”LiLin”);nscore:%fn-1.score);例1指向结构体变量的指针的应#include#include运行结果: :89101 name:LiLinvoidmain()定义指针变量p,structstudentlong

378、num;指ch向asrtruncat smteude2n0t ;chars类ex型;的f数lo据atsc structstudentstu_1;structstudent*p;p=&stu_1; stu_1.num=89101;strcpy(stu_1.name stu_1.sex=M;stu_指1.向sc的or结e=89.5;printf(No.:%ldnname:%snsex:%c,stu-1.sex,stu-1.num,stu-1.name构体变量中 ,stu的成员sex:score:89.500000 :89101 name:LiLin sex: score:89.500000prin

379、tf(No.:%ldnname:%snsex:%cnscore:%fn,(*p).num,(*p).name,(*p).sex,(*p).score);型数据的指针构体变量-的起 就是使指向- 赋值。第一个 各个成员的值。用 中的成员,依 函数也是用来输出 的是(*)图11-711.6 指指指向向结结构构体体类类型数据的指针程序分析:在函数的执行部分将结构体变量-的起 始地址赋给指针变量,也就是使指向-,然后对-的各成员赋值。第一个 函数是输出-的各个成员的值。用 -表示-中的成员,依 此类推。第二个函数也是用来输出-各成员的值,但使用的是(*) 这样的形式。程序分析:在函数的执行部分将结 始

380、地址赋给指针变量,也,然后对-的各成员函数是输出-的 -表示- 此类推。第二个 -各成员的值,但使用 这样的形式。图11-711.6 指向结构体类型数据的指针以下3种形式等价: 结构体变量成员名(*)成员名-成员名请分析以下几种运算:其中-称为指向运算符。-得到指向的结构体变量中的成员的值。-得到指向的结构体变量中的成员 的值,用完该值后使它加。-得到指向的结构体变量中的成员 的值加,然后再使用它。请分析以下几种运算:-得到指向的结构体变量中的成员的值。-得到指向的结构体变量中的成员 的值,用完该值后使它加。-得到指向的结构体变量中的成员 的值加,然后再使用它。11.6.2指向结构体数组的指针

381、构体数组的指针的应用mmmmeeee22220000;cccchhhhaaaarrrr sssseeeexxxx;iiiinnnntttt aaaaggggeeee; 3333=11110000111100001111,LLLLiiii LLLLiiiinnnn,MMMM,11118888,Fuuuunnnn,MMMM,11119999,11110000111100004444,WaWaWaWannnnggggMMMMiiiinnnngggg,11.6 指向结构体类型数据的指针例11.4 指向结运行结果: LiLin18aZhang Fun19uWangMing20#include num,

382、p-name, p-sex, p-age);型数据的指针构体类型数据的指针 初值为stu,也就是数。在第一次循环中输出执行,使自加 值为结构体数组stu的 行+后p的值等于stu 次循环中输出stu1的 p的值等于stu+2,再输 行+后,的值变为,不再执行循环。图11-8结11.6 指向结构体类程序分析:是指向struct student结构体类型数据的指针 变量。在for语句中先使的初值为stu,也就是数 组stu第一个元素的起始地址。在第一次循环中输出 stu0的各个成员值。然后执行,使自加 。加意味着p所增加的值为结构体数组stu的 一个元素所占的字节数。执行+后p的值等于stu1,指

383、向stu1。在第二次循环中输出stu1的 各成员值。在执行后,p的值等于stu+2,再输 出stu 2的各成员值。在执行+后,的值变为 stu +, 已不再小于stu+3了,不再执行循环。程序分析:是指向struct student 变量。在for语句中先使的 组stu第一个元素的起始地址 stu0的各个成员值。然后 。加意味着p所增加的 一个元素所占的字节数。执1,指向stu1。在第二 各成员值。在执行后, 出stu 2的各成员值。在执stu +, 已不再小于stu+3了 图11-811.6 指向结构体类型数据的指针注意:(1) 如果的初值为stu,即指向第一个元素, 则加后p就指向下一个元

384、素。例如:(+p)-num先使自加,然后得到它指向 的元素中的num成员值(即10102)。(p+)-num先得到-num的值(即10101),然后使自加,指向stu1。请注意以上二者的不同。注意:(1) 如果的初值为stu,即指向第一个元素, 则加后p就指向下一个元素。例如:(+p)-num先使自加,然后得到它指向 的元素中的num成员值(即10102)。(p+)-num先得到-num的值(即10101),然后使自加,指向stu1。请注意以上二者的不同。11.6 指向结构体类型数据的指针注意:(2) 程序已定义了是一个指向struct student 类型数据的指针变量,它用来指向一个str

385、uct student类型的数据,不应用来指向stu数组元素中的 某一成员。例如:1a;如果要将某一成员的地址赋给p,可以用强制类 型转换,先将成员的地址转换成p的类型。 例如:( *)0a;注意:(2) 程序已定义了是一个指向struct student 类型数据的指针变量,它用来指向一个struct student类型的数据,不应用来指向stu数组元素中的 某一成员。r例如:1a;如果要将某一成员的地址赋给p,可以用强制类 型转换,先将成员的地址转换成p的类型。 例如:( *)0a;11.6 指向结构体类型数据的指针11.6.3用结构体变量和指向结构体的指针 作函数参数将一个结构体变量的值

386、传递给另一个函数,有 3个方法:用结构体变量的成员作参数。 (2) 用结构体变量作实参。(3) 用指向结构体变量(或数组)的指针作实参,将结构体变量(或数组)的地址传给形参.11.6.2指向结构体数组的指针11.6 指向结构体类型数据的指针例11.5 有一个结构体变量stu,内含学生学号、姓名和3 门课程的成绩。要求在main函数中赋予值,在另一函数 print中将它们输出。今用结构体变量作函数参数。#include struct studentint num;char name20; float score3;11.6 指向结构体类型数据的指针uuuu.s.s.s.sccccorororor

387、eeee2222void main()void print(struct student); struct student stu;stu.num=12345;strcpy(stu.name,LiLin;stu.score0=67.5;stu.score1=89;st=78.6);print(stu);运行结果: 67.50000089.00000078.599998void print(struct student stu)printf(FORMAT,stu.num,stu.name, stu.score0, stu.score1,stu.score2); printf(n);作实参。例11

388、.6将上题改用指向结构体变量的指针运行结果:#include struct studentint num;char name20; float score3;stu=12345, LiLi,67.5,89,78.6; 67.50000089.00000078.599998void main()void print(struct student *);/*形参类型修改成指向结构体的指针变量*/ print(&stu);/*实参改为stu的起始地址*/void print(struct student *p)/*形参类型修改了*/printf(FORMAT,p-num,p-name,p-score

389、0,p-score1,p-score2);/*用指针变量调用各成员的值*/printf();型数据的指针体变量stu时赋初值,这 中的形参被定义为指 的指针变量。注意在调 量str的起始地址stu 地址传送给形参p(p是指在print函数中输出 个成员值,它们也就是赋值也可以改用scanf函图11-9。11.6 指向结构体类程序分析:此程序改用在定义结构体变量stu时赋初值,这 样程序可简化些。print函数中的形参被定义为指 向struct student类型数据的指针变量。注意在调 用print函数时,用结构体变量str的起始地址stu 作实参。在调用函数时将该地址传送给形参p(p是指 针

390、变量)。这样就指向stu。在print函数中输出 所指向的结构体变量的各个成员值,它们也就是 stu的成员值.main函数中的对各成员赋值也可以改用scanf函 数输入.程序分析:此程序改用在定义结构 样程序可简化些。print函数 向struct student类型数据 用print函数时,用结构体变 作实参。在调用函数时将该 针变量)。这样就指向stu 所指向的结构体变量的各 stu的成员值.main函数中的对各成员数输入.图11-9 11.7 用指针处理链表11.7.1链表概述链表是一种常见的重要的数据结构,是动 态地进行存储分配的一种结构。链表的组成:个元素结点:用户需要的实际数据和链

391、接节点 的指针图11-10头指针:存放一个地址,该地址指向一图11-10 11.7 用指针处理链表用结构体建立链表: struct studentint num;struct student *next ;;其中成员num和score用来存放结点中的有 用数据(用户需要用到的数据),next 是指针类型的成员,它指向struct student类型数据(这就是next所在的结 构体类型)图11-11float score;图11-11运行结果:1010189.51010390.0#include #define NULL 01010785.0struct studentlong num;flo

392、at score; struct student *next; ; main()struct student a,b,c,*head,*p; a. num=99101; a.score=89.5; b. num=99103; b.score=90; c. num=99107; c.score=85;head=&a;a.next=&b;b.next=&c; c.next=NULL;p=head;doprintf(%ld %5.1fn,p-num,p-score); p=p-next; while(p!=NULL); 11.7 用指针处理链表11.7.2简单链表 11.7 用指针处理链表程序分析:

393、开始时使head指向a结点,a.next指向b结点, b.next指向c结点,这就构成链表关系。 “c.next=NULL” 的作用是使c.next不指向任何有 用的存储单元。在输出链表时要借助p,先使p指向a 结点,然后输出a结点中的数据,“p=p-next” 是 为输出下一个结点作准备。p-next的值是b结点的 地址,因此执行“p=p-next”后p就指向b结点,所 以在下一次循环时输出的是b结点中的数据。程序分析:开始时使head指向a结点,a.next指向b结点, b.next指向c结点,这就构成链表关系。 “c.next=NULL” 的作用是使c.next不指向任何有 用的存储单元

394、。在输出链表时要借助p,先使p指向a 结点,然后输出a结点中的数据,“p=p-next” 是 为输出下一个结点作准备。p-next的值是b结点的 地址,因此执行“p=p-next”后p就指向b结点,所 以在下一次循环时输出的是b结点中的数据。 11.7 用指针处理链表11.7.3处理动态链表所需的函数库函数提供动态地开辟和释放存储单元的 有关函数:malloc函数其函数原型为void *malloc(unsigned int size);其 作用是在内存的动态存储区中分配一个长度为 size的连续空间。此函数的值(即“返回值”) 是一个指向分配域起始地址的指针(类型为 void)。如果此函数未

395、能成功地执行(例如内 存空间不足),则返回空指针(NULL)。 11.7 用指针处理链表(2) calloc函数其函数原型为void *calloc(unsigned , unsigned size);其作用是在内存的动态存储区 中分配个长度为size的连续空间。函数返回 一个指向分配域起始地址的指针;如果分配不 成功,返回NULL。用calloc函数可以为一维数组开辟动态存 储空间,n为数组元素个数,每个元素长度为 size 11.7 用指针处理链表(3) free函数其函数原型为void free(void *p);其作用 是释放由指向的内存区,使这部分内存区能 被其他变量使用。是最近一次

396、调用calloc或 malloc函数时返回的值。free函数无返回值.以前的版本提供的malloc和calloc函数 得到的是指向字符型数据的指针。 ANSI 提 供的malloc和calloc函数规定为void类型。理链表链表链表是指在程序执行过程中从 个链表,即一个一个地开辟结 据,并建立起前后相链的关系立一个有3名学生数据的单向动图11-12图11-12 11.7 用指针处11.7.4建立动态所谓建立动态 无到有地建立起一 点和输入各结点数例11.5写一函数建 态链表.算法如图 11.7 用指针处理链表算法的实现:成,该结点不应连于,则输入的是第 adp1,即把p1的值 指向新开辟的结点

397、p1链表中第一个结点图11-13我们约定学号不会为零,如果输入的学号为 ,则表示建立链表的过程完接到链表中。如果输入的p1-num不等 一个结点数据(n=1),令he 赋给head,也就是使head也 所指向的新开辟的结点就成为图11-13 11.7 用指针处理链表算法的实现:如果输入的p1-num,则应链入第个结点(n=2), 将新结点的地址赋给第一个结点的 next成员.接着使,也就是使指向刚才建 立的结点图11-14再开辟另一个结点并使p1指向它,接着输入该 结点的数据.图11-14 11.7 用指针处理链表第三次循环中,由于(),又的值赋给-,也就是将第 个结点连接到第个结点之后,并使

398、 ,使指向最后一个结点.图11-15算法的实现: 再开辟一个结点并使p1指向它,并输入该结点的 数据.在将 图11-15 11.7 用指针处理链表算法的实现:再开辟一个新结点,并使p1指向它,输入该结 点的数据。由于p1-num的值为,不再执行循环此新结点不应被连接到链表中.将NULL赋给p2-next.建立链表过程至此结束,p1最后所指的结点未链入链表中,第三个结点的next成员的值 为NULL,它不指向任何结点。图11-16,图11-16 11.7 用指针处理链表建立链表的函数如下:#include #include #define NULL 0 /令NULL代表,用它表示“空地址#def

399、ine LEN sizeof(struct student)/令LEN代表struct/student类型数据的长度 struct studentlong num;float score;struct student *next;int n; /n为全局变量,本文件模块中各函数均可使用它 11.7 用指针处理链表struct student *creat()struct student *head;struct student *p1,*p2;n=0; p1=p2=( struct student*) malloc(LEN); scanf(%ld,%f,&p1-num,&p1-score);

400、head=NULL;while(p1-num!=0)n=n+1;if(n=1)head=p1;else p2-next=p1; p2=p1;p1=(struct student*)malloc(LEN); scanf(%ld,%f,&p1-num,&p1-score);p2-next=NULL;return(head); 11.7 用指针处理链表11.7.5输出链表首先要知道链表第一个结点的地址,也就是知道head的值。然后设一个指针变量p,先指向一个结点,输出所指的结点,然后使后移 个结点,再输出,直到链表的尾结点。图11-17,11-18要第 一图11-17,11-18 11.7 用指针处

401、理链表例19编写一个输出链表的函数print.void print(struct student *head)struct student *p;printf(nNow,These %d records are:n,n); p=head;if(head!=NULL) doprintf(%ld %5.1fn,p-num,p-score); p=p-next;while(p!=NULL); 11.7 用指针处理链表11.7.6 对链表的删除操作图11-19从一个动态链表中删去一个结点,并不是真 正从内存中把它抹掉,而是把它从链表中分离开 来,只要撤销原来的链接关系即可。图11-19 11.7 用指

402、针处理链表例11.10写一函数以删除动态链表中指定的结点.解题思路:从p指向的第一个结点开始,检查该结点中的 num值是否等于输入的要求删除的那个学号。如果 相等就将该结点删除,如不相等,就将p后移一个 结点,再如此进行下去,直到遇到表尾为止。 11.7 用指针处理链表可以设两个指针变量p1和p2,先使p1指向 第一个结点 .如果要删除的不是第一个结点,则使p1后 移指向下一个结点(将p1-next赋给p1),在此 之前应将p1的值赋给p2 ,使p2指向刚才检查 过的那个结点 11.7 用指针处理链表注意:要删的是第一个结点(的值等于 的值,如图1-0()那样),则应将- 赋给。这时指向原来的

403、 第二个结点。第一个结点虽然仍存在,但它已与链 表脱离,因为链表中没有一个结点或头指针指向它。虽然还指向它,它仍指向第二个结点,但仍 无济于事,现在链表的第一个结点是原来的第二个 结点,原来第一个结点已“丢失” ,即不再是链表 中的一部分了。注意:要删的是第一个结点(的值等于 的值,如图1-0()那样),则应将- 赋给。这时指向原来的 第二个结点。第一个结点虽然仍存在,但它已与链 表脱离,因为链表中没有一个结点或头指针指向它。虽然还指向它,它仍指向第二个结点,但仍 无济于事,现在链表的第一个结点是原来的第二个 结点,原来第一个结点已“丢失” ,即不再是链表 中的一部分了。 11.7 用指针处理

404、链表注意:如果要删除的不是第一个结点,则将- 赋给-,见图10()。-原来指向指向的结点(图 中第二个结点),现在-改为指向 -所指向的结点(图中第三个结点)。 所指向的结点不再是链表的一部分。还需要考虑链表是空表(无结点)和链表中找 不到要删除的结点的情况。注意:如果要删除的不是第一个结点,则将- 赋给-,见图10()。-原来指向指向的结点(图中第二个结点),现在-改为指向 -所指向的结点(图中第三个结点)。 所指向的结点不再是链表的一部分。还需要考虑链表是空表(无结点)和链表中找 不到要删除的结点的情况。图11-20 11.7 用指针处理链表图11-20算法:图11-21 11.7 用指针

405、处理链表算法:图11-21 11.7 用指针处理链表删除结点的函数del:struct student *del(struct student *head,long num)struct student *p1,*p2;if (head=NULL)printf(nlist null!n);goto end; p1=head;while(num!=p1-num & p1-next!=NULL)p2=p1;p1=p1-next; if(num=p1-num)if(p1=head)head=p1-next; else p2-next=p1-next;printf(delete:%ldn,num);n

406、=n-1; else printf(%ld not been found!n,num);end;return(head); 11.7 用指针处理链表11.7.7对链表的插入操作对链表的插入是指将一个结点插入到一个已有 的链表中。为了能做到正确插入,必须解决两个问题: 怎样找到插入的位置; 怎样实现插入。 11.7 用指针处理链表先用指针变量p0指向待插入的结点,p1指向第 一个结点将p0-num与p1-num相比较,如果p0-nump1- num ,则待插入的结点不应插在p1所指的 结点之前。此时将p1后移,并使p2指向刚才p1 所指的结点. 11.7 用指针处理链表再将p1-num与p0-n

407、um比,如果仍然是p0-num 大,则应使p1继续后移,直到p0-p1- num为止。 这时将p0所指的结点插到p1所指结点之前。但是如 果p1所指的已是表尾结点,则p1就不应后移了。如 果p0- num比所有结点的num都大,则应将p0所指 的结点插到链表末尾。如果插入的位置既不在第一个结点之前,又不在表尾结点之后,则将p0的值赋给p2-next,使 p2-next指向待插入的结点,然后将p1的值赋给 p0-next,使得p0-next指向p1指向的变量 1111111111111111.77777777 用指针处理链表用指针处理链表用指针处理链表用指针处理链表用指针处理链表用指针处理链表用

408、指针处理链表用指针处理链表如果插入位置为第一个结点之前(即p1等于 head时),则将p0赋给head,将p1赋给p0-next 如果要插到表尾之后,应将p0赋给p1-next, NULL赋给p0-next图11-22图11-22算法:图11-23: 11.7 用指针处理链表算法 图11-23 11.7 用指针处理链表例11.11插入结点的函数insert如下。struct student *insert(struct student *head, struct student *stud)struct student *p0,*p1,*p2; p1=head;p0=stud;if(head=

409、NULL)head=p0; p0-next=NULL;elsewhile(p0-nump1-num) & (p1-next!=NULL)p2=p1;p1=p1-next; if(p0-numnum)if(head=p1) head=p0;else p2-next=p0;p0-next=p1; elsep1-next=p0; p0-next=NULL;n=n+1;return(head); 11.7 用指针处理链表11.7.8对链表的综合操作 将以上建立、输出、删除、插入的函数组织在一个C程序中,用函数作主调函数。void main()struct student *head,stu;longd

410、el_num; prinf(intput records:n) ; head=creat();print(head);printf ( n intput thedeleted number:n);scanf (%ld,&del_num) ;head=del(head,del_num); print(head);printf ( n intput the deleted number:n); scanf (%ld,&stu.num,&stu.score) ; head=insert(head,&stu);print(head); 11.7 用指针处理链表此程序运行结果是正确的。它只删除一个结 点

411、,插入一个结点。但如果想再插入一个结点, 重复写上程序最后4行,共插入两个结点,运行结 果却是错误的。Inputrecords:(建立链表) 10,10,10, 11.7 用指针处理链表Now,these 3 records are: 101010intput thedeleted number:10103(删除) :10Now,these 4 records are: 1010 11.7 用指针处理链表input the inserted record(插入第一个结点) 10102,90Now,these 3 records are: 101010input the inserted rec

412、ord(插入第二个结点) 10104,99Now,these 4 records are: 10101010 11.7 用指针处理链表出现以上结果的原因是: stu是一个有固定地址的结构体变量。第一次 把stu结点插入到链表中,第二次若再用它来插入第二个结点,就把第一次结点的数据 冲掉了,实际上并没有开辟两个结点。为了 解决这个问题,必须在每插入一个结点时新 开辟一个内存区。我们修改main函数,使之能删除多个结点(直到输入要删的学号为0),能插入多个结点(直到输入要插入的学 号为0)。 11.7 用指针处理链表main()struct student *head,*stu;long del_

413、num;printf(input records:n); head=creat();print (head);printf(ninput the deleted number:); scanf(%ld,&del_num);while (del_num!=0)head=del(head,del_num);print (head);printf (input the deleted number:); scanf(%ld,&del_num); printf(ninput the insertedrecord:);stu=(struct student *) malloc(LEN); scanf(%

414、ld,%f,&stu-num,&stu-score);while(stu-num!=0)head=insert(head,stu); printf(input the inserted record:);stu=(struct student *)malloc(LEN);scanf(%ld,%f,&stu-num,&stu-score); 11.7 用指针处理链表stu定义为指针变量,在需要插入时先用 malloc函数开辟一个内存区,将其起始地址经强 制类型转换后赋给stu,然后输入此结构体变量中 各成员的值。对不同的插入对象,stu的值是不同 的,每次指向一个新的struct student

415、变量。在 调用insert函数时,实参为head和stu,将已建立 的链表起始地址传给insert函数的形参,将stu( 即新开辟的单元的地址)传给形参stud,返回的 函数值是经过插入之后的链表的头指针(地址) 11.7 用指针处理链表运行结果: : 10,10,10, : 101010 11.7 用指针处理链表intput thedeleted number10103(删除) :10 Now,these 4 records are 10910intput thedeleted number10103(删除) :105 Now,these 4 records are 109 11.7 用指针

416、处理链表intput thedeletednumber:0input the insertedrecord 10104,87Now,these 3 records are 1010199.01010487input the insertedrecord 10106,65Now,these 3 records are 1010199.010104871010665.0占同一段内存的结构称为一般形式为:成员表列 量表列;图11-24图11-24 11.8 共用体11.8.1共用体的概念 使几个不同的变量共“共用体”类型的结构.定义共用体类型变量的 union共用体名变 11.8 共用体例如:uni

417、on dataunion data int i;int i;char ch; 或char ch;float f;float f;a,b,c;union data a,b,c; 11.8 共用体共用体和结构体的比较:结构体变量所占内存长度是各成员占的 内存长度之和。每个成员分别占有其自己的 内存单元。共用体变量所占的内存长度等于最长的 成员的长度。例如:上面定义的“共用体”变量、各占 个字节(因为一个实型变量占个字节),而不 是各占个字节。 11.8 共用体11.8.2共用体变量的引用方式 只有先定义了共用体变量才能引用它,而且不能引用共用体变量,而只能引用共用体变量中的 成员。例如:前面定义了

418、a、b、c为共用体变量a.i (引用共用体变量中的整型变量)a.ch(引用共用体变量中的字符变量)a.f (引用共用体变量中的实型变量)11.8共用体11.8.3共用体类型数据的特点(1)同一个内存段可以用来存放几种不同类型的成 员,但在每一瞬时只能存放其中一种,而不 是同时存放几种。(2) 共用体变量中起作用的成员是最后一次存放 的成员,在存入一个新的成员后原有的成员 就失去作用。(3) 共用体变量的地址和它的各成员的地址都是 同一地址。 11.8 共用体(4) 不能对共用体变量名赋值,也不能企图引用 变量名来得到一个值,又不能在定义共用体 变量时对它初始化。(5) 不能把共用体变量作为函数

419、参数,也不能使 函数带回共用体变量,但可以使用指向共用 体变量的指针(6) 共用体类型可以出现在结构体类型定义中, 也可以定义共用体数组。反之,结构体也可 以出现在共用体类型定义中,数组也可以作 为共用体的成员。 11.8 共用体例112 设有若干个人员的数据,其中有学生 和教师。学生的数据中包括:姓名、号码、 性别、职业、班级。教师的数据包括:姓名图11-25、号码、性别、职业、职务。可以看出,学 生和教师所包含的数据是不同的。现要求把图11-25它们放在同一表格中。算法:图11-26 11.7 用指针处理链表算法:图11-26 11.8 共用体#include structint num;

420、char name10; char sex;char job; unionint banji;char position10;category;person2;/*先设人数为2*/1111111111111111.88888888 共用体共用体共用体共用体共用体共用体共用体共用体void main()int i; for(i=0;i2;i+)scanf(%d %s %c %c, &personi.num, &personi.name,&personi.sex, &personi.job); if(personi.job = S)scanf(%d, &personi.category.banji

421、); else if(personi.job = T)scanf(%s, personi.category.position);printf(No. name sex job class/positionn); for(i=0;i2;i+)if (personi.job = S)printf(“%-6d%-10s%-3c%-3c%-6dn”,personi.num, personi.name, personi.sex, personi.job,personi.category.banji); elseprintf(“%-6d%-10s%-3c%-3c%-6sn”,personi.num,per

422、soni.name,personi.sex, personi.job, personi.category.position);else printf(“Input error!”); printf(n); 运行情况如下: 13.9 枚举类型枚举:将变量的值一一列举出来,变量的值只限于列举 出来的值的范围内。申明枚举类型用enumenum weekdaysun,mon,tue枚,举we元d,素thu,fri,sat;枚举常量定义变量:enum weekday workday,week-day; enumsun,mon,tue,wed,thu,fri,satworkday; 变量值只能是sun到s

423、at之一13.9 枚举类型说明: 在编译中,对枚举元素按常量处理,故称枚举 常量。它们不是变量,不能对它们赋值。(2) 枚举元素作为常量,它们是有值的,语言编译 按定义时的顺序使它们的值为,(3) 枚举值可以用来作判断比较。(4) 一个整数不能直接赋给一个枚举变量。例算法:13口袋中有红、黄、蓝、白、黑5种颜色的球若干个。每次从口袋中先后取出个球,问得到 3种不同色的球 的可能取法,输出每种排列的情况。图11-27,11-2813.9 枚举类型算法:图11-27,11-2811113333.9999 枚举类型枚举类型枚举类型枚举类型#include main()enum color red,y

424、ellow,blue,white,black;enum color i,j,k,pri; int n,loop;n=0; for (i=red;i=black;i+)for (j=red;j=black;j+)if (i!=j) for (k=red;k=black;k+) if (k!=i) & (k!=j)n=n+1;printf(%-4d,n);for (loop=1;loop=3;loop+)switch (loop)case 1: pri=i;break; case 2: pri=j;break;case 3: pri=k;break;default:break;11113333.9

425、999 枚举类型枚举类型枚举类型枚举类型switch (pri)case red:printf(%-10s,red); break;case yellow: printf(%-10s,yellow); break; case blue: printf(%-10s,blue); break;case white: printf(%-10s,white); break;case black: printf(%-10s,black); break; default :break;printf(n);printf(ntotal:%5dn,n);运行情况如下:1redyellowblue2redyell

426、owwhite3redyellowblack58blackwhitered59blackwhiteyellow60blackwhiteblue total:6013.10 用typedef定义类型用typedef声明新的类型名来代替已有的类型名声明INTEGER为整型 typedef int INTEGER 声明结构类型Typedef struct int month; int day;int year;DATE;13.10 用typedef定义类型声明为整型数组类型;声明为字符指针类型 typedef char *STRING;声明POINTER为指向函数的指针类型,该函数返回 整型值typ

427、edef int (*POINTER)()13.10 用typedef定义类型用typedef定义类型的方法 先按定义变量的方法写出定义体(如:int i)。 将变量名换成新类型名(例如:将i换成COUNT)。 在最前面加(例如:typedef int COUNT)。 然后可以用新类型名去定义变量。13.10 用typedef定义类型用typedef定义类型的方法(举例) 先按定义数组变量形式书写:int n100; 将变量名换成自己指定的类型名: intNUM0; 在前面加上typedef,得到typedef int NUM; 用来定义变量:NUM;13.10 用typedef定义类型说明:

428、 用typedef可以声明各种类型名,但不能用 来定义变量。(2) 用typedef只是对已经存在的类型增加一个类型名,而没有创造新的类型。(3) 当不同源文件中用到同一类型数据时,常用 typedef声明一些数据类型,把它们单独放在一个文件 中,然后在需要用到它们的文件中用#include命令把 它们包含进来。(4) 使用typedef有利于程序的通用与移植。13.10 用typedef定义类型说明:(5) typedef与#define有相似之处,例如:typedef int COUNT;#define COUNT int的作用都是 用COUNT代表int。但事实上,它们二者是不同的。#d

429、efine是在预编译时处理的,它只能作简单的字符串替换,而typedef是在编译时处理的。实际上它并不是 作简单的字符串替换,而是采用如同定义变量的方法 那样来声明一个类型第十二章主要内容12.1位运算符和位运 算12.2位运算举例12.3位段概念位运算是指按二进制位进行的运算。因为在 系统软件中,常要处理二进制位的问题。例如:将一个存储单元中的各二进制位左移 或右移一位,两个数按位相加等。语言提供位运算的功能,与其他高级语言(如PASCAL)相比,具有很大的优越性。12.1 位运算符和位运算运算符含义运算符含义按位与取反|按位或右移语言提供的位运算符有:说明:(1)位运算符中除以外,均为二目

430、(元)运算符, 即要求两侧各有一个运算量。(2)运算量只能是整型或字符型的数据,不能为实型 数据。12.1.1“按位与”运算符()按位与是指:参加运算的两个数据,按二进制位进行“ 与”运算。如果两个相应的二进制位都为,则该位 的结果值为;否则为。即:,例:并不等于,应该是按位与运算:00000011(3)& 00000101(5) 00000001(1)3&5的值得注意:如果参加&运算的是负数(如-3&-5),则要以 补码形式表示为二进制数,然后再按位进行“与”运算。两个数按位与的用途:(1) 清零若想对一个存储单元清零,即使其全部二进制位 为,只要找一个二进制数,其中各个位符合以下 条件:原

431、来的数中为的位,新数中相应位为。 然后使二者进行运算,即可达到清零目的。00101011& 10010100 00000000例: 原有数为,另找一个数,设它为,这样在原 数为的位置上,该数的相 应位值均为。将这 进行运算:(2) 取一个数中某些指定位如有一个整数(个字节),想要取其 中的低字节,只需将与8个1按位与即可。0 0 1 0 1 1 0 01 0 1 0 1 1 000 0 0 0 0 0 0 01 1 1 1 1 1 110 0 0 0 0 0 0 01 0 1 0 1 1 00a b c(3)保留一位的方法:与一个数进行运算, 此数在该位取。例:有一数,想把其中左面第、位保留下

432、来,运算如下:01010100(84)& 00111011(59) 00010000(16)即:a=84,b=59c=a&b=1612.1.2“按位或”运算符(|)两个相应的二进制位中只要有一个为,该位 的结果值为。即 |,|,|,|例: 060|017,将八进制数60与八进制数17进行按位或运算。00110000| 00001111 00111111应用:按位或运算常用来对一个数据的某些位定值为。例如:如果想使一个数 的低位改为,只需将与进行 按位或运算即可。例: 是一个整数(位), 有表达式: | 则低位全置为,高位保留原样。12.1.3“异或”运算符()异或运算符也称XOR运算符。它的规

433、则是: 若参加运算的两个二进制位同号则结果为(假)异号则结果为(真)即:00=0,01=1,10=1, 11=000111001 00101010 00010011例:即:071052=023(八进制数)运算符应用:运算结果的低位正好 是原数低位的翻转。可 见,要使哪几位翻转就将 与其进行运算的该几位 置为即可。01111010 00001111 01110101()使特定位翻转设有,想使其低位翻转,即变为,变为。可以将它与 进行运算,即:()与相,保留原值00001010 00000000 00001010例如:01200=012因为原数中的与进行运算得, 得,故保留原数。() 交换两个值,

434、不用临时变量例如:,。 想将和的值互换,可以用以下赋值语句实现: ab;()(ab的结果,a已变成)()(ba的结果,b已变成)()(ab的结果,a已变成)ba; ab;即等效于以下两步:执行前两个赋值语句:“;”和“;”相当于b=b(ab)。 再执行第三个赋值语句: 。由于a的 值等于(),b的值等于(), 因此,相当于a=,即a的值等 于,等于。得到原来的值。12.1.4“取反”运算符()是一个单目(元)运算符,用来对一个二进 制数按位取反,即将变,将变。例如,是对八进制数(即二进制数 )按位求反。() 00000000000101011111111111101010 (八进制数17775

435、2)L6L7L8 L912.1.5左移运算符()左移运算符是用来将一个数的各二进制 位全部左移若干位。例如:a=)右移运算符是a2表示将a的各二进制位 右移2位,移到右端的低位被舍弃,对无符号 数,高位补0。例如:a=017时:a的值用二进制形式表示为00001111, 舍弃低2位11:a2=00000011右移一位相当于除以2右移n位相当于除以2n。在右移时,需要注意符号位问题:对无符号数,右移时左边高位移入0;对于 有符号的值,如果原来符号位为0(该数为正), 则左边也是移入0。如果符号位原来为1(即负 数),则左边移入0还是1,要取决于所用的计算 机系统。有的系统移入0,有的系统移入1。

436、移 入0的称为“逻辑右移”,即简单右移;移入1的 称为“算术右移”。例:a的值是八进制数113755:a:1001011111101101(用二进制形式表示) a1: 0100101111110110(逻辑右移时)a1: 1100101111110110(算术右移时)在有些系统中,a1得八进制数045766,而在 另一些系统上可能得到的是145766。Turbo C和其 他一些C编译采用的是算术右移,即对有符号数右 移时,如果符号位原来为1,左面移入高位的是1。12.1.7位运算赋值运算符位运算符与赋值运算符可以组成复合赋值运算符。 例如: &=, |=, =, =, =例: a & = b相

437、当于 a = a & b a =2相当于a = a 4目的是使要取出的那几位移到最右端未右移时的情况右移4位后的情况 设置一个低4位全为1,其余全为0的数。 ( 0 4 )程序如下:#include void main() unsigned a,b,c,d;scanf(“%o”,&a); b=a4; c=(0 4)& ( 0 4 ),%dn”,a,a,d,d);运行情况如下:(输入) , 217 (的值), 13(的值)输入的值为八进制数331, 其二进制形式为11011001 经运算最后得到的d为00001101 即八进制数,十进制数13。例12.2 循环移位要求将进行右循环移位将右循环移

438、位,即将中原来 左面() 位右移位,原来 右端位移到最左 面位。步骤: 将的右端位先放到中的高位中, 实现语句:(); 将右移位,其左面高位位补, 实现语句:; 将与进行按位或运算,即|;运行情况如下: , 3程序如下:#include void main() unsigned a,b,c;int n; scanf(“a=%o,n=%d”,&a,&n); b=an;b;tf(“%on%o”,a,c);c=c| 运行开始时输入八进制数157653,prin即二进制数1101111110101011循环右移位后得二进制数0111101111110101即八进制数7576512.3 位段信息的存取一

439、般以字节为单位。实际上,有时 存储一个信息不必用一个或多个字节,例如,“真” 或“假”用或表示,只需位即可。在计算机用 于过程控制、参数检测或数据通信领域时,控制信 息往往只占一个字节中的一个或几个二进制位,常 常在一个字节中放几个信息。怎样向一个字节中的一个或几个二进制位赋值和改 变它的值呢?可以用以下两种方法:可以人为地将一个整型变量data分为几部分。但是用这种方法给一个字节中某几位赋 值太麻烦。可以位段结构体的方法。(2)位段C语言允许在一个结构体中以位为单位来指定其成员所占内存长度,这种以位为单位的成 员称为“位段”或称“位域”( bit field) 。利用 位段能够用较少的位数存

440、储数据。程序如下:structpacked-data unsigned :; unsigned :; unsigned :; unsigned :; int ;data;关于位段的定义和引用的说明:(1)位段成员的类型必须指定为unsigned或int类型。(2) 若某一位段要从另一个字开始存放,可用以下形 式定义:unsigned:1;unsignedb:;一个存储单元 unsigned:;unsigned:;另一存储单元a、b、c应连续存放在一个存储单元中,由于用了长 度为的位段,其作用是使下一个位段从下一个存 储单元开始存放。因此,只将a、b存储在一个存储 单元中,c另存在下一个单元(“

441、存储单元”可能是一 个字节,也可能是2个字节,视不同的编译系统而 异)。关于位段的定义和引用的说明:(3) 一个位段必须存储在同一存储单元中,不能 跨两个单元。如果第一个单元空间不能容 纳下一个位段,则该空间不用,而从下一 个单元起存放该位段。(4) 可以定义无名位段。(5) 位段的长度不能大于存储单元的长度,也不 能定义位段数组。(6) 位段可以用整型格式符输出。(7) 位段可以在数值表达式中引用,它会被系统 自动地转换成整型数。第十三章本章要点文件的基本概念文件的基本函数文件的顺序读写文件的随机读写文件简单应用主要内容13.1文件概述13.2文件类型指针13.3文件的打开与关闭13.4文件

442、的读写13.5文件的定位13.6出错的检测13.7文件输入输出小结13.1 C文件概述文件的定义 所谓文件一般指存储在外部介质(如磁盘磁带)上 数据的集合. 操作系统是以文件为单位对数据进行管理的.输入文件缓冲区输出文件缓冲区程序数据区文件13.1 C文件概述(续)文件的分类从用户观点: 特殊文件(标准输入输出文件或标准设备文件) 普通文件(磁盘文件)从操作系统的角度看,每一个与主机相连的输入 输出设备看作是一个文件。 例:输入文件:终端键盘输出文件:显示屏和打印机13.1 C文件概述(续)文件的分类按数据的组织形式: ASCII文件(文本文件):每一个字节放一个ASCII代码 二进制文件:把

443、内存中的数据按其在内存中的存储形 式原样输出到磁盘上存放. 例:整数10000在内存中的存储形式以及分别按ASCII 码形式和二进制形式输出如下图所示:13.1 C文件概述(续)文件的分类ASCII文件和二进制文件的比较: ASCII文件便于对字符进行逐个处理,也便于输出 字符。但一般占存储空间较多,而且要花费转换时 间。 二进制文件可以节省外存空间和转换时间,但一个 字节并不对应一个字符,不能直接输出字符形式。一般中间结果数据需要暂时保存在外存上,以后又 需要输入内存的,常用二进制文件保存。13.1 C文件概述(续)文件的分类C语言对文件的处理方法: 缓冲文件系统:系统自动地在内存区为每一个

444、正 在使用的文件开辟一个缓冲区。用缓冲文件系统 进行的输入输出又称为高级磁盘输入输出。非缓冲文件系统:系统不自动开辟确定大小的缓 冲区,而由程序为每个文件设定缓冲区。用非缓 冲文件系统进行的输入输出又称为低级输入输出 系统。13.1 C文件概述(续)说明:在UNIX系统下,用缓冲文件系统来处理文本文件, 用非缓冲文件系统来处理二进制文件.ANSI C 标准只采用缓冲文件系统来处理文本文件和二进制文件. C语言中对文件的读写都是用库函数来实现.13.2 文件类型指针Turbo 在stdio.h文件中有以下的文件类型声明: typedef struct shortlevel;*缓冲区“满”或“空”

445、的程度*unsignedflags; *文件状态标志* charfd;*文件描述符*unsignedcharhold; *如无缓冲区不读取字符* shortbsize;*缓冲区的大小* unsignedchar*buffer;/*数据缓冲区的位置*/ unsignedar*curp;/*指针,当前的指向*/ unsignedistemp;/*临时文件,指示器*/ shorttoken;/*用于有效性检查*/FILE;在缓冲文件系统中,每个被使用的文件都要在内存中开辟一 FILE类型的区,存放文件的有关信息.13.2 文件类型指针(续)FILE类型的数组:FILEf5;定义了一个结构体数组f,它

446、有5个元素, 可以用来存放5个文件的信息。文件型指针变量:FILE*fp;fp是一个指向FILE类型结构体的 指针变量。可以使fp指向某一个文件的结构体变量,从而通过该结构体变量中的文件信息能够访问该文件。如果 有个文件,一般应设个指针变量,使它们分别指向 个文件,以实现对文件的访问。13.3 文件的打开与关闭一.文件的打开(fopen函数) 函数调用:FILE*fp; fpfopen(文件名,使用文件方式);需要打开的文件名,也就是准备访问的文件的名字;使用文件的方式(“读”还是“写”等);让哪一个指针变量指向被打开的文件。13.3 文件的打开与关闭(续)文件使用方式含义“r”(只读)为输入

447、打开一个文本文件“w”(只写)为输出打开一个文本文件“a”(追加)向文本文件尾增加数据“rb”(只读)为输入打开一个二进制文件“wb”(只写)为输出打开一个二进制文件ab“(追加)向二进制文件尾增加数据r+“(读写)为读/写打开一个文本文件 w+”(读写)为读/写建立一个新的文本文件 a+”(读写)为读/写打开一个文本文件 rb+“(读写)为读/写打开一个二进制文件“wb+“(读写)为读/写建立一个新的二进制文件“ab+”(读写)为读/写打开一个二进制文件13.3 文件的打开与关闭(续)二.文件的关闭(fclose函数)函数调用: fclose(文件指针); 函数功能:使文件指针变量不指向该文

448、件,也就是文件指针变 量与文件“脱钩”,此后不能再通过该指针对原来与 其相联系的文件进行读写操作返回值: 关闭成功返回值为;否则返回EOF(-1)13.4 文件的读写一.字符输入输出函数(fputs()和fgets()fputs函数 函数调用:fputs ( ch,fp ) ;函数功能: 将字符(ch的值)输出到fp所指向的文件中去。 返回值: 如果输出成功,则返回值就是输出的字符; 如果输出失败,则返回一个EOF.13.4 文件的读写(续)fgets函数函数调用: chfgets(fp); 函数功能:从指定的文件读入一个字符,该文件必须是以读或 读写方式打开的。返回值: 读取成功一个字符,赋

449、给。如果遇到文件结束符,返回一个文件结束标志EOF 。13.4 文件的读写(续)常见的读取字符操作从一个文本文件顺序读入字符并在屏幕上显示出来: ch = fgetc(fp);while(ch!=EOF)putchar(ch);ch = fgetc(fp);注意:EOF不是可输出字符,因此不能在屏幕上显示。 由于字符的ASCII码不可能出现,因此EOF定义为是合适的。当读入的字符值等于时,表示读入 的已不是正常的字符而是文件结束符。13.4 文件的读写(续)常见的读取字符操作从一个二进制文件顺序读入字符: while(!feof(fp)ch = fgetc(fp);注意:ANSI C提供一个f

450、eof()函数来判断文件是否 真的结束。如果是文件结束,函数feof(fp)的值为(真);否则为(假)。以上也适用于文本文件的读取。11113333.4444 文件的读写文件的读写文件的读写文件的读写(续续续续)fputc和fgetc函数使用举例:例3从键盘输入一些字符,逐个把它们送到 磁盘上去,直到输入一个“”为止。#include #include voidmain(void)FILE *fp;char ch,filename10; scanf(%s,filename); if(fp=fopen(filename,w)=NULL) printf(cannot open filen); ex

451、it(0); /*终止程序*/ch=getchar( ); /*接收执行scanf语句时最后输入的回车符 */while(ch!=#fputc(ch,fp);putchar(ch); ch=getchar(); fclose(fp);ch=getchar( ); /* 接收输入的第一个字符 */运行情况如下:(输入磁盘文件名) (输入一个字符串)(输出一个字符串)11113333.4444 文件的读写文件的读写文件的读写文件的读写(续续续续)fputc和fgetc函数使用举例:例3将一个磁盘文件中的信息复制到另一个磁 盘文件中。#include #include main( )FILE *in

452、,*out;char ch,infile10,outfile10; printf(Enter the infile name:n); scanf(%s,infile);printf(Enter the outfile name:n); scanf(%s,outfile);if(in=fopen(infile,r)=NULL)printf(cannot open infilen); exit(0); if(out=fopen(outfile,w)=NULL)printf(cannot open outfilen); exit(0); while(!feof(in)fputc(fgetc(in),

453、out); fclose(in);fclose(out);运行情况如下: (输入原有磁盘文件名 n: (输入新复制的磁盘文件名)程序运行结果是将文件中的内容复制到 中去。11113333.4444 文件的读写文件的读写文件的读写文件的读写(续续续续)fputc和fgetc函数使用举例:例3的改进:复制一个二进制文件,利用main 参数,在输入命令行是将两个文件名输入。#include #include main(int argc,char *argv )FILE *in,*out; char ch;if (argc!=3)printf(cannot open infilen); exit(0)

454、;if(out=fopen(argv2,wb)=NULL)printf(cannot open outfilen); exit(0);while(!feof(in) fputc(fgetc(in),out);fclose(in); fclose(out);printf(You forgot to enter a filenamen); exit(0); if(in=fopen(argv1,rb)=NULL)运行方法: 设经编译连接后得到的可执行文件名为a.exe,则在DOS命令工 作方式下,可以输入以下的命令行:a 和,分别输入到 和中,的内容为a,的 值等于 。13.4 文件的读写(续)二.

455、数据块读写函数(fread()和fwrite() 函数调用:fread (buffer,size,count,fp);fwrite(buffer,size,count,fp); 参数说明: buffer:是一个指针。对fread 来说,它是读入数据的存放地址。 对fwrite来说,是要输出数据的地址(均指起始地址)。 size:要读写的字节数。count: 要进行读写多少个size字节的数据项。 fp:文件型指针。13.4 文件的读写(续)使用举例:若文件以二进制形式打开: fread(f,4,2,fp);此函数从fp所指向的文件中读入2个4个字节的数 据,存储到数组f中。13.4 文件的读写

456、(续)使用举例:若有如下结构类型: struct student_typechar name10; int num;int age;char addr30;stud40;可以用fread和fwrite来进行数据的操作: for(;) fread(&studi,sizeof(struct student-type),1,fp); for(;,) fwrite(&studi,sizeof(struct student-type),1,fp);13.4 文件的读写(续)使用举例: 例3从键盘输入个学生的有关数据,然后把它们转存 到磁盘文件上去。#include #define SIZE 4 stru

457、ct student_typechar name10;int num; int age;char addr15;studSIZE; /*定义结构*/11113333.4444 文件的读写文件的读写文件的读写文件的读写(续续续续)void save( )FILE *fp; int i;if(fp=fopen(stu-list,wb)=NULL) printf(cannot open filen); return; for(i=0;iSIZE;i+)/*二进制写*/if(fwrite(&studi,sizeof(struct student_type),1,fp)!=1)printf(“file

458、write errorn”);/*出错处理*/fclose(fp); /*关闭文件*/ main()int i;for(i=0;iSIZE;i+)/*从键盘读入学生信息*/ scanf(%s%d%d%s,studi.name,&studi.num,&studi.age,studi.addr);save( );/*调用save()保存学生信息*/运行情况如下: 输入个学生的姓名、学号、年龄和地址:Z-F-T L- -11113333.4444 文件的读写文件的读写文件的读写文件的读写(续续续续)验证在磁盘文件“-”中是否已存在此数据, 用以下程序从“-”文件中读入数据,然后在 屏幕上输出。#in

459、clude #define SIZE 4 struct student_typechar name10;int num; int age;char addr15;int i; FILE*fp;fp=fopen(stu-list,rb);for(i=0;iSIZE;i+)fread(&studi,sizeof(struct student_type),1,fp) printf(%-10s %4d%4d%-15sn,studi.studi.num,studi. age,studi.addr); fclose (fp);studSIZE; main( )屏幕上显示出以下信息:Z-F-T L- -;n

460、am13.4 文件的读写(续)如果已有的数据已经以二进制形式存储在一个磁盘文件 “-”中,要求从其中读入数据并输出到 “-”文件中,可以编写一个函数, 从磁盘文件中读二进制数据。void load( )FILE *fp;int i;if(fp=fopen(stu-dat,rb)=NULL) printf(cannot open infilen); return;for(i=0;iSIZE;i+)if(fread(&studi,sizeof(struct student_type),1,fp)!=1)if(feof(fp) fclose(fp); return; printf(file read

461、 errorn);fclose (fp); 13.4 文件的读写(续)三.格式化读写函数(fprintf()和fscanf() 函数调用:从磁盘文件中读入或输出字符。 例: fprintf(fp,”%d,%6.2f”,i,t);Fscanf (fp,”%d,%f”,&i,&t);fprintf ( 文件指针,格式字符串,输出表列); fscanf( 文件指针,格式字符串,输入表列);函数功能:注意: 用fprintf和fscanf函数对磁盘文件读写,使用方便,容易理解, 但由于在输入时要将ASCII码转换为二进制形式,在输出时又要 将二进制形式转换成字符,花费时间比较多。因此,在内存与磁 盘频

462、繁交换数据的情况下,最好不用fprintf和fscanf函数,而 用fread和fwrite函数。13.4 文件的读写(续)数)。putw函数定义如下: putw(int I,FILE *fp)char s; s=&I;putc(s0,fp);putc(s1,fp); return i;gutw函数定义如下: gutw(FILE *fp)char s; s=char *&i;s0 = getc(fp);s1 = getc(fp); return i;三.其他读写函数 putw()和getw() 函数调用:putw(int i,FILE * fp); int i = getw(FILE * fp

463、);函数功能:对磁盘文件中读写一个字(整例:putw(10,fp);i = getw(fp);13.4 文件的读写(续)用户自定义读取其他类型数据的函数向磁盘文件写一个实数(用二进制方式)的函数putfloat : putfloat(float num,FILE *fp)char s;int count;s = (char*)#for(count = 0;count 4;count+) putc(scount,fp);13.4 文件的读写(续)fgets函数 函数作用:从指定文件读入一个字符串。函数调用: fgets(str,n,fp);从fp指向的文件输入n-1个字符,在最后加一个0

464、返回值: str的首地址13.4 文件的读写(续)fputs函数 函数作用:向指定的文件输出一个字符串。函数调用: fgets(“china”,fp);第一个参数可以是字符串常量、字符数组名或字符型 指针。字符串末尾的不输出。返回值: 输入成功,返回值为0; 输入失败,返回EOF.rewind函数 函数作用:使位置指针重新返回文件的开头,无返回值。应用举例: 例3有一个磁盘文件,第一次将它的内容显示在屏幕 上,第二次把它复制到另一文件上。13.5 文件的定位#include main()FILE *fp1,*fp2;fp1=fopen(file1.c,r);fp2=fopen(file2.c,

465、w); while(!feof(fp1) putchar(getc(fp1); rewind(fp1);while(!feof(fp1)putc(getc(fp1),fp2); fclose(fp1);fclose(fp2);13.5 文件的定位顺序读写和随机读写 顺序读写:位置指针按字节位置顺序移动。随机读写: 读写完上一个字符(字节)后,并不一定要读写其 后续的字符(字节),而可以读些文件中任意位置 上所需要的字符(字节)。13.5 文件的定位fseek函数(一般用于二进制文件) 函数功能:改变文件的位置指针 函数调用形式:fseek(文件类型指针,位移量,起始点)起始点:文件开头SEEK

466、_SET0文件当前位置SEEK_CUR1文件末尾SEEK_END2位移量:以起始点为基点,向前移动的字节数。一般要求为long型13.5 文件的定位fseek函数应用举例fseek(fp,100L,); 将位置指针移到离文件头100个字节处 fseek(fp,50L,); 将位置指针移到离当前位置50个字节处 fseek(fp,50L,); 将位置指针从文件末尾处向后退个字节11113333.5555 文件的定位文件的定位文件的定位文件的定位例3在磁盘文件上存有个学生的数据。要求 将第、个学生数据输入计算机,并 在屏幕上显示出来。#include#include structstudent_t

467、ypecharname10; intnum;intage; charsex;stud10;main()inti; FILE*fp;if(fp=fopen(stud-dat,rb)=NULL)printf(cannotopenfilen); exit(0);for(i=0;i10;i+=2)fseek(fp,i*sizeof(structstudent_type),0); fread(&studi,sizeof(structstudent_type),1,fp);printf(“%s%d%d%cn”,studi.name,studi.num,studi.age,studi.sex); fclos

468、e(fp)13.5 文件的定位ftell函数函数作用: 得到流式文件中的当前位置,用相对于文件开头的位 移量来表示。返回值: 返回当前位置,出错时返回-1L。 应用举例:i = ftell(fp);if(i=-1L) printf(“errorn”);13.6 出错的检测ferror函数调用形式: ferror(fp); 返回值:返回0,表示未出错;返回非0,表示出错。在调用一个输入输出函数后立即检查ferror函数的值, 否则信息会丢失。在执行fopen函数时,ferror函数 的初始值自动置为。13.6 出错的检测clearerr函数调用形式: clearerr(fp); 函数作用:使文件

469、错误标志和文件结束标志置为0。只要出现错误标志,就一直保留,直到对同一文件 调用clearerr函数或rewind函数,或任何其他一个输 入输出函数。13.7 文件输入输出小结分类函数名功能打开文件fopen()打开文件关闭文件fclose()关闭文件文件定位fseek()改变文件位置指针的位置Rewind()使文件位置指针重新至于文件开头Ftell()返回文件位置指针的当前值文件状态feof()若到文件末尾,函数值为真Ferror()若对文件操作出错,函数值为真Clearerr()使ferror和feof()函数值置零13.7 文件输入输出小结分类函数名功能文件读写fgetc(),getc(

470、)从指定文件取得一个字符 fputc(),putc()把字符输出到指定文件 fgets()从指定文件读取字符串 fputs()把字符串输出到指定文件 getw()从指定文件读取一个字(int型) putw()把一个字输出到指定文件 fread()从指定文件中读取数据项 fwrite()把数据项写到指定文件中 fscanf()从指定文件按格式输入数据 fprintf()按指定格式将数据写到指定文件中第十四章主要内容14.常见错误分析14.程序调试14.1常见错误分析忘记定义变量。输入输出的数据的类型与所用格式说明符不一 致。未注意型数据的数值范围。在输入语句scanf中忘记使用变量的地址符。输入

471、数据的形式与要求不符。误把“”作为“等于”运算符。语句后面漏分号。在不该加分号的地方加了分号。对应该有花括号的复合语句,忘记加花括号。括号不配对。14.1常见错误分析(11) 在用标识符时,忘记了大小写字母的区别。(12) 引用数组元素时误用了圆括号。(13) 在定义数组时,将定义的“元素个数”误认为是“ 可使用的最大下标值”。(14) 对二维或多维数组的定义和引用的方法不对。(15) 误以为数组名代表数组中全部元素。(16) 混淆字符数组与字符指针的区别。(17) 在引用指针变量之前没有对它赋予确定的值。(18)switch语句的各分支中漏写break语句。 (19) 混淆字符和字符串的表示

472、形式。(20) 使用自加(+)和自减(-)运算符时出的错误。14.1常见错误分析(21) 所调用的函数在调用语句之后才定义,而又在 调用前未声明。(22) 对函数声明与函数定义不匹配。(23) 在需要加头文件时没有用#include命令去包 含头文件。(24) 误认为形参值的改变会影响实参的值。(25) 函数的实参和形参类型不一致。(26) 不同类型的指针混用。(27) 没有注意函数参数的求值顺序。(28) 混淆数组名与指针变量的区别。(29) 混淆结构体类型与结构体变量的区别,对一个 结构体类型赋值。14.1常见错误分析(21) 所调用的函数在调用语句之后才定义,而又在 调用前未声明。(22

473、) 对函数声明与函数定义不匹配。(23) 在需要加头文件时没有用#include命令去包 含头文件。(24) 误认为形参值的改变会影响实参的值。(25) 函数的实参和形参类型不一致。(26) 不同类型的指针混用。(27) 没有注意函数参数的求值顺序。(28) 混淆数组名与指针变量的区别。(29) 混淆结构体类型与结构体变量的区别,对一个 结构体类型赋值。(30) 使用文件时忘记打开,或打开方式与使用情况 不匹配。14.1常见错误分析程序出错有3种情况: 语法错误 逻辑错误 运行错误14.2 程序调试所谓程序调试是指对程序的查错和 排错。调试程序步骤: 先进行人工检查,即静态检查。 上机调试。 在改正语法错误和“警告”后,程 序经过连接(link)就得到可执行 的目标程序。运行程序,输入程序 所需数据,就可得到运行结果。

展开阅读全文
相关资源
正为您匹配相似的精品文档
相关搜索

最新文档


当前位置:首页 > 中学教育 > 其它中学文档

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号