新書推薦:
《
别怕,试一试
》
售價:HK$
67.9
《
人才基因(凝聚30年人才培育经验与智慧)
》
售價:HK$
103.4
《
深度学习详解
》
售價:HK$
114.8
《
李白传(20世纪文史学家李长之经典传记)
》
售價:HK$
45.8
《
津轻:日本无赖派文学代表太宰治自传性随笔集
》
售價:HK$
66.7
《
河流之齿
》
售價:HK$
59.8
《
新经济史革命:计量学派与新制度学派
》
售價:HK$
89.7
《
盗墓笔记之秦岭神树4
》
售價:HK$
57.3
|
內容簡介: |
本书共分3篇:初级篇(C语言基础知识及程序设计初步)、提高篇(C语言中的复杂数据类型与高级编程技术)和实训篇(基于C语言面向过程的项目实训)。 本书在内容编排上对C语言的知识不求全面系统,但求有理论、有深度、有层次,使其真正能够承担起第一门专业软件课程的重任,对大一新生有一定的专业引导性;特别是实训篇的内容在目前已有教材中未曾见到过。 本书中的所有程序都是在VC++ 6.0环境下编译调试通过的。本着少而精的原则,全书版面清晰、结构紧凑,知识信息含量高。本书可以作为高校(本科、专科)计算机及其相关专业的教材,也可以作为非计算机专业的教学参考书、培训班教材和自学教材。
|
目錄:
|
初级篇 C语言基础知识及程序设计初步
第1章 绪论 3
1.1 程序设计概述 3
1.1.1 程序设计的发展历程 3
1.1.2 结构化程序设计 4
1.1.3 面向对象程序设计 5
1.1.4 程序设计语言 6
1.1.5 语言处理程序 8
1.1.6 C语言简介 8
1.2 算法及算法的表示 9
1.2.1 算法的概念 9
1.2.2 流程图表示法 9
1.2.3 简单算法设计 11
1.3 C语言基础 13
1.3.1 字符集 13
1.3.2 标识符 13
1.3.3 关键字 14
1.3.4 运算符 14
1.3.5 分隔符 14
1.3.6 空白符 15
1.3.7 C程序的基本结构 15
1.4 上机步骤与实验指导 18
1.4.1 程序的编辑、编译、运行和调试 18
1.4.2 Visual C集成开发环境 19
1.4.3 编辑、编译、运行和调试程序 19
第2章 基本数据类型 24
2.1 数据类型概述 24
2.2 运算符和表达式 25
2.2.1 常量 25
2.2.2 变量 25
2.2.3 整型 26
2.2.4 实型 27
2.2.5 字符型 28
2.2.6 void类型 29
2.2.7 运算符概述 30
2.2.8 赋值运算符和赋值表达式 30
2.2.9 算术运算符和算术表达式 32
2.2.10 关系运算符和关系表达式 34
2.2.11 逻辑运算符和逻辑表达式 34
2.2.12 条件运算符和条件表达式 35
2.2.13 逗号运算符和逗号表达式 35
2.2.14 类型转换 36
2.3 数据的输入与输出 37
2.3.1 字符输入输出函数 38
2.3.2 格式输出函数printf 39
2.3.3 格式输入函数scanf 41
2.3.4 简单程序举例 43
习题 46
第3章 3种程序控制结构 48
3.1 顺序结构 48
3.2 选择结构 49
3.2.1 if语句实现选择结构 49
3.2.2 switch语句实现选择结构 58
3.3 循环结构 61
3.3.1 while语句 62
3.3.2 dowhile语句 64
3.3.3 for语句 65
3.3.4 break语句和continue语句 67
3.3.5 循环嵌套 69
3.4 程序结构综合举例 71
习题 77
第4章 复杂数据类型初探数组 79
4.1 复杂数据类型概述 79
4.2 一维数组 79
4.2.1 一维数组的定义 79
4.2.2 一维数组元素的引用 80
4.2.3 一维数组的初始化 81
4.2.4 一维数组程序举例 82
4.3 二维数组 85
4.3.1 二维数组的定义 85
4.3.2 二维数组元素的引用 85
4.3.3 二维数组的初始化 86
4.3.4 二维数组程序举例 87
4.4 字符串与数组 90
4.4.1 字符数组的定义 90
4.4.2 字符数组的初始化 90
4.4.3 字符数组的使用 92
4.4.4 字符串处理函数 93
4.4.5 字符数组程序举例 95
习题 97
提高篇 C语言中的复杂数据类型与高级编程技术
第5章 函数 101
5.1 函数的定义 101
5.2 函数的调用 104
5.2.1 函数原型声明 104
5.2.2 函数的调用 106
5.2.3 参数传递机制 107
5.2.4 函数返回值 111
5.3 函数的嵌套调用 112
5.4 递归函数 113
5.5 变量的作用域 117
5.5.1 局部作用域 118
5.5.2 全局作用域 118
5.6 变量的存储方式和生存期 121
习题 127
第6章 指针 129
6.1 指针的基本概念 129
6.1.1 地址和指针的概念 129
6.1.2 指针变量的定义 130
6.1.3 指针变量运算符 131
6.1.4 指针的运算 135
6.2 指针与数组 138
6.2.1 指向数组的指针 138
6.2.2 指针与字符数组 141
6.2.3 多级指针与指针数组 144
6.2.4 指针与多维数组 149
6.2.5 数组指针 153
6.3 指针与函数 154
6.3.1 指针作为函数参数 154
6.3.2 指针作为函数的返回值指针函数 157
6.3.3 指向函数的指针函数指针 158
6.4 动态内存分配与指向它的指针变量 162
习题 164
第7章 编译预处理 165
7.1 宏定义 165
7.1.1 无参宏 166
7.1.2 有参宏 168
7.2 文件包含 171
7.3 条件编译 172
习题 175
第8章 复杂数据类型结构体、共用体和枚举类型 176
8.1 结构体类型 176
8.2 结构体变量 178
8.2.1 结构体变量的定义 178
8.2.2 结构体变量的初始化 180
8.2.3 结构体变量成员的引用 181
8.3 结构体数组 184
8.3.1 结构体数组的定义 184
8.3.2 结构体数组的初始化 185
8.3.3 结构体数组应用举例 185
8.4 结构体指针 186
8.4.1 指向结构体变量的指针 187
8.4.2 指向结构体数组的指针 188
8.4.3 用结构体变量和结构体指针作为函数参数 189
8.5 用结构体指针处理链表 191
8.5.1 链表概述 191
8.5.2 创建单链表 193
8.5.3 输出链表 196
8.5.4 链表的删除操作 196
8.5.5 链表的插入操作 198
8.5.6 链表的综合操作 200
8.6 共用体类型 202
8.6.1 共用体的概念 202
8.6.2 共用体类型和共用体类型变量的定义 202
8.6.3 共用体成员的引用方式 204
8.6.4 共用体类型的特点和应用 204
8.7 枚举类型 207
8.7.1 枚举类型及枚举变量的定义 207
8.7.2 枚举元素的引用 208
习题 209
实训篇 基于C语言面向过程的项目实训
第9章 项目实训概述及数据存储基础 213
9.1 概述 213
9.2 最基本的数据存储技术文件 213
9.2.1 概述 213
9.2.2 文件类型 214
9.2.3 文件指针 214
9.2.4 文件的打开与关闭 214
9.2.5 文件的读写 215
9.2.6 文件的随机读写 218
第10章 需求分析与功能模块设计 220
10.1 需求分析 220
10.2 功能模块设计 220
10.2.1 功能模块划分 220
10.2.2 概要设计 221
第11章 详细设计与实现 223
11.1 概述 223
11.2 登录业务模块功能及主函数实现 223
11.2.1 系统登录及主函数功能流程图 223
11.2.2 代码实现步骤 223
11.3 系统菜单功能实现 226
11.3.1 功能需求描述 226
11.3.2 代码实现步骤 226
11.4 开户功能实现 227
11.4.1 功能需求描述 227
11.4.2 代码实现步骤 227
11.5 存款功能与取款功能的实现 231
11.5.1 存款功能需求描述 231
11.5.2 存款代码实现步骤 231
11.5.3 取款功能需求描述 232
11.6 文件的读写操作 234
11.6.1 文件的读写功能需求描述 234
11.6.2 文件的读写功能代码实现步骤 234
11.6.3 与读写文件相关的其他函数 235
11.7 查询功能实现 236
11.7.1 查询功能需求描述 236
11.7.2 查询功能代码实现步骤 237
11.8 销户功能实现 241
11.8.1 销户功能需求描述 241
11.8.2 销户功能代码实现步骤 241
第12章 项目测试与总结 244
12.1 项目测试 244
12.1.1 单元测试 244
12.1.2 集成测试 245
12.2 项目总结 246
12.2.1 实训目标与要求 246
12.2.2 实训过程与步骤 247
12.2.3 实训项目总结报告撰写要求 247
12.3 系统局限性分析与功能扩展的考虑 247
12.3.1 系统局限性分析 247
12.3.2 如何进行功能扩展的考虑 248
附录A 标准字符ASCII表 251
附录B 运算符和结合性 253
附录C 常见库函数 255
附录D 模拟考试题 261
模拟考试题一 261
模拟考试题一参考答案 271
模拟考试题二 272
模拟考试题二参考答案 283
附录E 参考课时安排 285
参考文献 286
|
內容試閱:
|
在编写本书之前,本书作者已在高校从事多年的高级语言(C、C、C#)程序设计、数据结构、面向对象程序设计及软件项目开发等计算机专业软件类课程的教学及研究工作。当站在软件工程及软件项目开发的高度,回过头来重新审视C语言,对于由该语言来承担计算机专业本科生软件系列第一门课程的重任,有了更深入的理解。但目前已有的关于C语言的教材大多只罗列了C语言的语法和应用实例,而不重视编程能力的培养,故组织编写了本书,旨在对C语言的知识体系重新梳理整合,在内容安排及教学深度等方面满足本科计算机及相关专业C语言程序设计课程的教学目标,使学生从专业的角度重视该课程并为后续课程的学习打下坚实的基础。本书共分3篇,即初级篇(C语言基础知识及程序设计初步)、提高篇(C语言中的复杂数据类型与高级编程技术)和实训篇(基于C语言面向过程的项目实训)。初级篇(第1~4章)介绍C语言的入门基础。我们将C语言的语法、基本数据类型、结构化编程思想、3种控制结构和数组都归属于C语言的入门基础。初学者通过本篇的学习,能够掌握C语言中最基础的主要知识点,有能力编制出结构清晰、代码规范的小程序,获得学习的自信。在C语言的学习过程中,学习并掌握数组是重中之重,数组也是初学者认识数据结构的起点,所以我们将数组归纳到了初级篇中。提高篇(第5~8章)主要涉及函数、指针和结构体等知识。介绍利用函数组织程序结构,实现模块化设计的方法;利用数组及结构体描述大量数据,使用链表组织数据,使学习者有能力完成较大规模的程序。指针让C语言体现出紧凑和灵活的特点,这一点在语言中至关重要。有了数组、指针和函数的综合使用,C语言才更加鲜活。在提高篇中还将学习一些经典算法的思想和实现,使学生进一步地掌握调试程序的方法,能够在一定程度上掌握部分C语言的高级编程技术。实训篇(第9~12章)是我们在这套教材中特别设计的项目实训部分,希望能够规范教师的教学指导与要求,通过一周的项目实训,使学生理论联系实际及编程的能力均能得到良好的训练。实训项目的目标是设计一个模拟的银行储蓄业务系统。学生在老师的指导下,学会利用C语言解决实际问题;帮助初学者真正提高程序设计能力,并初步接触软件工程的思想,做出一个小规模的综合性的应用项目。本书在内容编排上对C语言的知识不求全面系统,但求有理论、有深度、有层次,使其真正能够承担起第一门专业软件课程的重任,对大一新生有一定的专业引导性;特别是实训篇的内容在目前已有教材中未曾见到过。本书可以作为高校(本科、专科)计算机及其相关专业的教材,也可以作为非计算机专业的教学参考书、培训班教材和自学教材。本书由河北建筑工程学院丁学钧、温秀梅任主编并完成统稿,宋淑彩、高丽婷任副主编。参加编写者有:李耀辉(第1~2章),赵巍(第3章),付江龙(第4章、附录A~D),宋淑彩(第5章),高丽婷(第6章),温秀梅(第7章),庞慧(第8章),董颢霞(第9章),丁学钧(第10~12章、附录E)。参加本书部分编写工作的还有李建华、周丽莉等。在本书的大纲讨论和分工编写过程中,我们始终相互帮助,彼此鼓励,是一次非常难忘的经历。另外,本书在编写过程中引用了许多文献,谨向有关专家学者致以诚挚的谢意。感谢您选择本书。由于我们的水平有限,书中难免有疏漏、错误和不妥之处,恳请广大读者和专家提出批评和修改意见。对于选用本书的教师,我们提供电子版课件,可从清华大学出版社网站(www.tup.com.cn)下载。编 者2016年9月
3种程序控制结构第1章中已经介绍过,在结构化程序设计中所有程序都可以由3种基本结构组成,并给出了3种基本结构对应的流程图(图1.1、图1.2、图1.3)。本章介绍3种基本程序控制结构的C语言实现。3.1 顺 序 结 构顺序结构是最简单、最基本的结构。顺序结构是按照C语句出现的次序顺序执行每条语句,直至程序结束。【例3.1】 顺序结构程序举例(求表达式的值)。程序如下。#includeint main{int a,b,result;printf"please input two numbers:\n";scanf"%d,%d",&a,&b;result=a-2*b 3;printf"the result is:%d\n",result;return 0;}程序的运行结果如图3.1所示。
图3.1 例3.1的运行结果【例3.2】 求方程ax2 bx c=0的根,其中a、b、c由键盘输入,设b2?4ac>0。程序如下。#include#include 包含数学计算库函数的头文件int main{int a,b,c;三个系数double x1,x2,disc,p,q;printf"请输入方程三个系数a,b,c的值:";scanf"%d%d%d",&a,&b,&c;disc=b*b-4*a*c;p=-b2.0*a;2.0是为了保障不同类型的转换int-doubleq=sqrtdisc2.0*a;x1=p q;x2=p-q;printf"方程的根为:%8.2lf%8.2lf\n",x1,x2;return 0;}程序的运行结果如图3.2所示。
图3.2 例3.2的运行结果3.2 选 择 结 构选择结构也称为分支结构,它的作用是根据是否满足给定条件,选择一组操作执行。C语言提供了两种选择语句用于实现选择结构,分别是if(条件语句)和switch(开关语句)。3.2.1 if语句实现选择结构1.if语句的两种基本形式基本形式一如下。 if 表达式语句基本形式二如下。 if 表达式语句1 else 语句2说明:(1)表达式通常为关系表达式或逻辑表达式,也可以是其他表达式。基本形式一的执行过程是,首先计算表达式的值,若表达式的值为true(非0),则执行语句;否则,跳过语句,整个if语句终止执行,然后执行if语句的后继语句。基本形式二的执行过程是,首先计算表达式的值,若表达式的值为true(非0),则执行语句1;否则,执行语句2。(2)语句称作if语句的子语句,它可以是单语句、块语句(由花括号{ }括起来的多条语句组成,也称为复合语句)或空语句(只有一个分号的语句)。(3)对于基本形式二,每次运行程序时,语句1和语句2不会都被执行,只能执行其一。【例3.3】 根据用户的输入决定向用户显示什么样的信息。程序如下。#includeint main{char response;printf"Do you like programming?Y or N:";scanf"%C", &response;ifresponse==''Y''||response==''y'' printf"Just wait until you see what you can do!\n";ifresponse==''N''||response==''n''{ printf"Keep trying! I bet you''ll change your mind.\n"; printf"Programming can be blast!\n";}return0;}程序的运行结果如图3.3所示。图3.3 例3.3的运行结果程序解析:* 在这个程序中,response是一个字符变量,用来存储用户通过键盘输入的任何字符。用户的响应通过语句scanf%C, &response;输入到变量response中。* if语句用来判断用户输入的字符。询问变量response的值等于字母Y或y吗?这个问题,用C语言表达式表达成:response==''Y''||response==''y''注意:Y或y不是程序设计中的一个变量,它用于比较变量response中保存的值,所以使用单引号括起来,是字符常量。* 由于第二个if语句在条件为真时要执行的语句多于一条,因此,需要使用花括号将这两条语句括起来,构成块语句。【例3.4】 使用if-else结构形式改写上例。程序如下。#includeint main{char response;printf"Do you like programming?Y or N:";scanf"%C", &response;ifresponse==''Y''||response==''y'' printf"Just wait until you see what you can do!\n";else{ printf"Keep trying! I bet you''ll change your mind.\n"; printf"Programming can be blast!\n";}return0;}程序解析:在这个程序中,原程序中的第二条if语句用else取代,使程序的逻辑结构更加清晰,代码的可读性更好。注意:(1)一般情况下,if语句中的表达式常常是一个判断两个数据是否相等的关系表达式,要当心不要将==写成=。这是初学者最容易犯的错误!(2)第一条if语句后面只有一条语句,可以省略不用加外边的{ }。但是,对于初学者来说,建议任何情况下都不要省略程序段(块语句或单一语句)外边的{ }。因为,如果在必须加{ }的某个程序段外边忘记加了,程序的逻辑就会出现异常。下面关于块语句再做几点进一步的讨论。1)关于变量的定义位置在标准C中规定变量定义必须放在所有的执行语句之前!一旦在运行语句之后再有变量定义,则系统会报错误!【例3.5】 标准C中规定变量定义位置举例。程序如下。#include?int main{char char1=''A'';printf"大写字符%c的ASCII码: %d\n",char1,char1;char char2=char1 32;printf"小写字符%c的ASCII码: %d\n",char2,char2;return 0;}将上述源程序命名为3_5.c,在VC 6.0中调用C编译器编译该程序,将出现编译错误,如图3.4所示。
图3.4 变量定义位置错误时的编译错误示例程序解析:* 这是因为在上述程序中关于变量char2的定义语句:char char2=char1 32;放在了执行语句:printf"大写字符%c的ASCII码: %d\n",char1,char1;的后面而导致的错误。* 如果将上述源程序命名为3_5.cpp,在VC 6.0中则会调用C编译器,编译就不会出错。这是因为编译器认为上述代码是C语法格式的,也就是说在C中没有严格要求变量定义一定要放在执行语句之前。* 这就是C标准的问题,C89规定:需要在任何执行语句之前,声明所有局部变量。但是,在C99以及C中则没有这个限制,即在首次使用之前,可在程序的任何位置声明变量。* 如果交换上述程序中的第5、6行语句的位置,就不会出现编译错误,程序运行结果如图3.5所示。2)在块语句中定义变量块语句就是以花括号{ }包围起来的代码段,也称为block。block是允许出现在程序任意位置的,故在一个block前面很可能有多条可执行语句。但是只要在block开始的地方定义变量,即在block内部的可执行语句前面定义变量就不会出现编译错误。该变量的作用域和生存期只在该block里,且该变量可以屏蔽block外面的同名变量。
图3.5 改正例3.5变量定义位置后的程序运行结果如果将例3.5改写成以下形式,将第6、7行放入一对花括号{ }内,构成一个block,编译时就不会出现错误提示,程序运行结果与图3.5相同。#includeint main{char char1=''A'';printf"大写字符%c的ASCII码: %d\n",char1,char1;{ char char2=char1 32; printf"小写字符%c的ASCII码: %d\n",char2,char2;}return 0;}3)在块中定义变量的作用域与生命期关于作用域与生命期的概念将在第5章中详细介绍,这里只针对在块中定义变量的问题做简单讨论。在block中定义的变量的作用域和生存期只在该block里,且该变量可以屏蔽block外面的同名变量。为了说明这个问题,我们将对例3.5做如例3.6的变化,请读者通过程序中的注释及程序运行结果来加深问题的理解。【例3.6】 块中定义变量的作用域与生命期举例。程序如下。#include?int main{char char1=''A'';char char2=''B'';第一次(在块外)定义变量char2,并赋初值printf"大写字符%c的ASCII码: %d\n",char1,char1;printf"大写字符%c的ASCII码: %d\n",char2,char2;{char char2=char1 32; 第二次(在块内)定义变量char2,并赋值, 该变量将屏蔽掉块外面定义的同名变量,该变量的作用域从定义处起始至块尾标志的}处结束printf"小写字符%c的ASCII码: %d\n",char2,char2;此时输出的是块内定义的char2的值} 从块中退出后,块内定义的变量char2生命期结束,不复存在printf"测试变量char2的ASCII码值: %d\n",char2; 此时输出的是块外定义的char2的ASCII码值return 0;}程序的运行结果如图3.6所示。
图3.6 例3.6程序的运行结果2.if语句的嵌套一个if 语句可以用作另一个if 语句的子语句,这个if 语句称作嵌套的if 语句。嵌套的if 语句在使用时一定要谨慎,必须记住,当出现if语句嵌套时,不管书写格式如何,else都将与它前面最靠近的未曾配对的if语句相配对,构成一条完整的if语句。例如:if a==bif a==cprintf"a==b==c\n";elseprintf"a!=b\n";从书写格式上看,程序员想使else与if(a == b)相匹配,但实际情况是else与第二条if语句即if(a==c)相匹配的。如果想使else与if(a == b)相匹配,这段程序应写成:if a==b{ifa==cprintf"a==b==c\n";}elseprintf"a!=b\n";说明:当嵌套层次很多时,使用块语句,不仅可以避免错误,而且可以增加程序的可读性和清晰性。【例3.7】 下面是一个猜数程序,当用户猜对正确的数时,打印**Right**;否则打印** Wrong **,并告诉用户所猜的数比正确的数是大还是小。程序如下。#includeint main{int magic=17;int guess;printf"guess the magic number:";scanf"%d", &guess;ifguess==magic{printf"**Right**\n";printf"The magic number is %d\n", magic;}else{printf"**Wrong**\n";ifguessmagicprintf"Too high\n";elseprintf"Too low\n";}return0;}程序的运行结果如图3.7所示。 (a)(b)
(c)图3.7 例3.7的运行结果【例3.8】 下面这个程序输入某个学生三门课的成绩,计算其平均值,然后根据其值打印出评语。程序如下。#includeint main{ int math,chem,phy; double ave; 平均值 printf"Enter the scores:"; scanf"%d%d%d",&math, &chem, &phy; ave=math chem phy3.0 0.5;对平均值进行四舍五入 ifave=90 printf"Excellent\n"; else { ifave=80 && ave=70 && ave=60 && ave语句1 else if表达式2语句2 else if表达式n语句n else语句n 1如图3.9所示,在这种嵌套形式中,若表达式1的值为true,则执行语句1;若为false,则判断表达式2,其结果为true,执行语句2;若为false,则判断下一个if语句。若所有表达式的值都为false,则执行语句n 1,即if语句中的第n个else部分。
图3.9 if语句嵌套形式【例3.9】 使用if语句嵌套形式改写例3.8。程序如下。#includeint main{int math,chem,phy;double ave; 平均值printf"Enter the scores: ";scanf"%d%d%d", &math, &chem, &phy;ave=math chem phy3.0 0.5;对平均值进行四舍五入ifave=90printf"Excellent\n";else ifave=80printf"Good\n";else ifave=70printf"Average\n";else ifave=60printf"Pass\n";elseprintf"Fail\n";return0;}程序解析:* 例3.9程序的代码明显优于例3.8,结构清晰且提高了可读性。* 还应注意例3.9程序中if语句或else if语句中进行条件进行测试的表达式的变化,由例3.8中的逻辑表达式变化为条件表达式,简化了条件测试的表达,请读者注意理解这种变化。3.2.2 switch语句实现选择结构switch语句是多分支选择语句,又称开关语句,是选择结构的另一种形式。它将一个表达式的值与某些常量进行连续测试,如果某一常量与该表达式的值匹配,则与之相应的语句便被执行。switch语句的一般形式如下。 switch表达式 { case 常量表达式1: 语句组1 break; case 常量表达式2: 语句组2 break; case 常量表达式n: 语句组n break; default:语句组n 1 }其中语句组称为switch语句的子语句。在switch语句中,表达式值的类型和常量的类型必须一致,且只能是字符型、整型或枚举类型(枚举类型将在第8章中介绍)。当表达式的值与case中指定的常量相等时,就执行相应case后的语句,直到遇到break语句或者到达switch语句的末尾。如果出现相等匹配都不成功的情况,则执行default后的语句。default语句是可选项,如果没有default语句,那么当所有匹配都失败的情况下,不执行任何操作,程序直接执行switch语句之后的语句。图3.10描述了switch语句的流程。
图3.10 switch语句流程图注意:(1)switch下面的一对花括号不能省略,它的作用是将多分支结构视为一个不可分割的整体。每一个case后面的语句块不需要用大括号括起来,程序流程会自动顺序地执行该case后面的所有可执行语句。(2)每个case中的break语句使switch语句只执行一个case中的语句,执行到break语句即从switch语句中跳出。若没有break语句,将继续执行该case下面各case部分的执行语句。(3)所有常量表达式的值必须互不相同,否则编译时会发生错误。case部分与default部分的顺序可以自由书写。如果default部分位于程序最后,default部分的break语句便可以省略;否则break语句必不可少。【例3.10】 switch语句使用举例。程序如下。#includeint main{char ch;printf"Enter ''m'' 、 ''n'' or ''h'' or other: ";scanf"%c",&ch;switchch{ case ''m'': printf"Good morning!\n"; break; case ''h'': printf"Hello!\n"; break; case ''n'': printf"Good night!\n"; break; default: printf"I can''t understand.\n"; } printf"All done!\n"; return0;}程序的运行结果如图3.11所示。(a)(b)
(c)图3.11 例3.10程序的运行结果当若干个case所执行的内容可用一条语句(可以是块语句)表示时,允许这些case共用一条语句。这种情况下的switch 结构变为如下形式。 switch表达式 { case 常量表达式1: case 常量表达式2: case 常量表达式m:语句组m;break; case 常量表达式m 1: 语句组m 1; break; case 常量表达式n: 语句组n; break; default:语句组n 1; }这种结构的switch语句的流程是:当表达式的值与常量表达式1的值或常量表达式2的值,,或常量表达式m的值之一匹配时,都执行语句组m;当表达式的值为其他值时执行情况不变。【例3.11】 改写例3.10,允许用户输入字母时不区分大小写。程序如下。#includeint main{char ch;printf"Enter ''m'' 、 ''n'' or ''h'' or other: ";scanf"%c",&ch;switchch{ case ''m'':case ''M'': printf"Good morning!\n"; break; case ''H'': case ''h'': printf"Hello!\n"; break; case ''N'': case ''n'': printf"Good night!\n"; break; default: printf"I can''t understand.\n"; } printf"All done!\n"; return0;}程序解析:* 程序运行时允许用户输入大小写字母,即输入m或M时程序输出结果一样。同理,输入h或H、输入n或N,程序输出结果也是一样的。3.3 循 环 结 构循环结构是指在一定条件成立的情况下,重复执行某个程序段的结构。一般来说,一个循环结构由4个主要部分构成。(1)循环的初始部分。它保证循环结构能够开始执行的语句,往往编写在程序的开头部分,逻辑上先从这一部分开始执行。(2)循环的工作部分,即循环体,完成循环程序的主要工作。(3)循环的修改部分。它保证循环体在循环过程中,有关的变量能按一定的规律 变化。(4)循环的控制部分。它保证循环程序按规定的循环条件控制循环正确进行。对于一个具体的程序,上述几个部分有时很明显就能分开,有时却很难分开。相互位置可前可后,或相互包容,但循环的初始部分一般应在循环的前面。C语言中构成循环结构的语句有while语句、dowhile语句和for语句。3.3.1 while语句while语句的一般形式如下。 while表达式语句循环体while语句首先计算表达式的值,在其值为真true(非0)时,执行循环体中的语句,而在其值为假为false(0)时,终止循环的执行,程序接着执行循环体后的语句。注意:(1)while后面的括号里是表达式而不是语句,表达式是没有分号的。(2)循环体语句可以是简单语句、空语句或块语句。(3)第一次计算表达式的值,如果为false0,则循环体一次也不执行。【例3.12】 编写程序计算1 2 3 99 100。程序如下。#includeint main{int sum=0;int n=1;whilen{sum =n;n;}printf"sum=%d\n",sum;return 0;}程序的运行结果如图3.12所示。程序解析:* 循环体如果包含多条语句,一定要用花括号括起来,以复合语句的形式出现。如果不用花括号,则循环体只有一条语句。例如,本例中while循环体语句中如果无花括号,则循环体只有一条语句sum =n;。
图3.12 例3.12的运行结果* 在循环体中应有使循环趋向于结束的语句。例如,在本例中循环结束的条件是n100,因此在循环体中应该有使n增值以最终能使n100的语句,例中使用n;语句来达到此目的。如果无此语句,则n的值始终不改变,循环永不结束,构成死循环,程序不能正常结束。* 做累加时,存放结果的变量一般称为累加器,初始化为0;而做累乘时(如求阶乘),存放结果的变量一般称为累乘器,初始化为1。课堂思考与练习:(1)计算100以内偶数之和应如何修改程序?(2)计算10!。【例3.13】 分别统计出输入的所有正整数中小于60和大于等于60的数据的个数,然后显示。程序如下。#includeint main{int x;int count1=0, count2=0;定义两个计数器printf"请输入正整数,当输入0或负数时结束输入:";scanf"%d",&x; 读入导入值whilex0{ifxelsecount2;scanf"%d",&x;循环读入数据}printf"小于60的正整数个数:%d\n",count1;printf"大于等于60的正整数个数:%d\n",count2;return 0;}程序的运行结果如图3.13所示。程序解析:* 程序中,进入while循环之前有下列一条数据输入语句:scanf"%d",&x;
图3.13 例3.13的运行结果该语句为while循环读入了一个导入值,这是构成循环必不可少的。* while条件判断中用输入零或负数作为循环的结束标志,零或负数也常常被称为哨兵,是编写这类循环结构的典型用法。因为while循环常用于事先无法确切地知道循环次数的情况,必须要有一个承担循环结束标志的哨兵。* 循环体中又有一条数据输入语句,scanf"%d",&x;看似重复,但是不能省略,否则,x永远为第一次输入的值,可能会构成死循环。3.3.2 dowhile语句dowhile语句的一般形式如下。 do { 语句循环体 }while表达式;dowhile语句和while语句类似,只是对循环条件的检查在循环尾部进行。其执行过程是,先执行一次循环体语句,然后计算表达式的值,当表达式的值为true(非0)时,返回重新执行循环体语句,如此反复,直到表达式的值为false(0)为止,此时循环结束。注意:(1)dowhile循环的循环体至少执行一次。(2)while(表达式)后面的分号不可省略。【例3.14】 使用dowhile语句重写例3.12。程序如下。#includeint main{int sum=0;int n=1;do{sum =n;n;}whilenreturn 0;}程序的运行结果与图3.12相同。程序解析:对于一个循环问题,既可以用while循环,也可以用dowhile循环来解决问题。while语句和dowhile语句的区别在于:while语句条件测试在先,如果这个测试为假,则循环体一次也不执行。dowhile语句的条件测试在后,其循环体至少执行一次。课堂思考与练习:(1)用dowhile循环计算100以内偶数之和。(2)用dowhile循环计算10!。3.3.3 for语句for语句的一般形式如下。 for表达式1; 表达式2; 表达式3语句循环体圆括号内由分号隔成三个部分,每部分是一个表达式。其中表达式2用于控制循环执行,当其值为真时,重复执行循环体,而在其值为假时,终止循环语句的执行,程序转去执行该语句之后的语句。for循环的执行过程可以用while循环来说明。表达式1;while表达式2{语句表达式3;}即在进入for循环时,表达式1求值一次,然后对表达式2进行求值,如果表达式2的值为真,执行for循环的循环体中的语句,随后对表达式3求值,紧接着又对表达式2求值,由表达式2的值决定是否进行下次循环。for语句是C语言中使用最灵活方便的一种循环语句,它不仅用于循环次数已知的情况,还能用于循环次数预先不能确定而只给出循环结束条件的情况。注意:(1)for语句中的3个表达式必须用分号隔开,其中表达式1一般用来初始化循环控制变量。(2)表达式2为表示循环条件的表达式,用作循环条件控制,其作用与前两类循环语句中的表达式完全一样,用法也基本相同。(3)表达式3用来修改循环控制变量,用以表示循环控制变量的增量或减量,常用自增或自减运算符。(4)for语句中的3个表达式允许全部省略或部分省略,以充分体现其灵活性,但用作分隔符的两个分号绝不能省略。但是建议初学者尽量地按照标准格式来使用,培养自己编写出专业、规范的代码。另外,当for语句的循环体中只有1条语句时,循环体外面的{}可以省略,甚至可以将这1条语句与 for语句写在同一行,但是要知道这不是一个好的编程习惯。【例3.15】 用for循环重写例3.12。程序如下。#includeint main{int sum=0,n;forn=1; nsum =n;printf"sum=%d\n",sum;return 0;}程序的运行结果与图3.12相同。程序解析:* 语句forn = 1; n int main{double degCel;摄氏intdegFahr; 华氏for degFahr=0; degFahrreturn0;}程序的运行结果如图3.14所示。
图3.14 例3.16程序运行部分结果的截图3.3.4 break语句和continue语句break语句有两个用途,一种是用于switch语句中,前面已经作了介绍。另一种用途是用在循环语句的循环体中。当在循环体中遇到break语句时,循环立即终止,程序跳过break语句后的其余语句,从循环语句后的第一条语句继续执行。continue语句只用于循环语句的循环体中,当在循环体中遇到continue语句时,程序跳过continue语句后的其余语句,开始下一次循环。注意:(1)在实际应用中,break语句和continue一般都是与if条件语句配合使用的。当正常的循环终止条件尚未满足,而满足了其他给定的条件(由if条件语句给出的)时,使程序流程在循环的中途强行退出本次循环。(2)break语句也可以用于嵌套的循环结构中,在这种情况下,如执行break,则仅仅退出包含该break语句的那层循环,即break语句不能使程序控制退出一层以上的循环。下面给出的这个例子较好地说明了break语句和continue语句的用法。【例3.17】 输出100~200之间不能被3整除的数,只输出前20个即可。程序如下。#includeint main{int i;int count=0; 计数器,两个作用:1.统计已输出数据的个数 2.用于控制输出格式fori=100; i{ifi%3==0 continue;当i能被3整除时,结束本次循环,进入下一次循环count;ifcount20break; 当已输出20个数据后强行退出整个循环printf"%-6d",i;ifcount%5==0 printf"\n";一行输出5个数据}return 0;}程序的运行结果如图3.15所示。
图3.15 例3.17的运行结果程序解析:* 当i能被3整除时,执行continue语句,结束本次循环(即跳过ifcontinue之后的4条语句),进行下一次循环,继续寻找满足条件的数据。只有n不能被3整除时才执行ifcontinue后面的这4条语句。* 100~200之间不能被3整除的数共有68个,但是题目要求只输出前20个数据。那么循环多少次就恰好输出前20个呢?程序员事先是不知道的。所以只能使用了break语句,与if条件语句配合,找到前20个满足条件的数据后就不必让循环继续了,强行中断整个循环。注意:continue和break的作用有相似之处,却也有着根本区别,continue语句只是结束本次循环,还要进行下一次循环,而不是要结束整个循环;break语句则是结束整个循环,转而执行循环体外的其他语句。3.3.5 循环嵌套一个循环体内包含另一个完整的循环结构称为循环的嵌套,内嵌的循环中还可以嵌套循环,这就是多重循环。【例3.18】 输出100以内的所有素数。算法分析:(1)判断一个数m是否是素数,让m被2到之中的整数去除,如果m能被这些数之中的任何一个整除,则说明m不是素数;否则m是素数。(2)利用穷举法,对2~100之间(1不是素数,2是最小的素数)的每一个自然数进行素数的判断,若是,则输出之。(3)上述步骤(1)用一个内循环实现,步骤(2)用一个外循环实现。程序如下。#include#include标准库函数,包含数学函数,本例中用到了求平方根函数sqrt#define NUMBER 100 定义符号常量,作为区间上限int main{int m,n;m外循环变量2~100,n内循环变量2~kint k; 为减少循环次数,令k=sqrtmint flag=1;设置素数判断标志,1为素数,0为非素数int count=0; 计数器form=2; mm外循环变量2~100{k=intsqrtm 1; 加1是为避免可能出现的误差 flag=1;注意每次进入内循环之前都要重新赋值flag=1forn=2; n内循环:判断一个数m是否是素数{ifm%n==0 {flag=0; 标志非素数break; 跳出当前循环(内循环)}}内循环结束ifflag==0 continue;跳过当前循环结构(外循环)中后面的语句,进入下一次循环(外循环)printf"%-5d",m; 输出素数count;ifcount%10==0 printf"%\n";}外循环结束printf"\n***%d以内共有%d个素数***\n",NUMBER,count;return 0;}程序的运行结果如图3.16所示。
图3.16 例3.18的运行结果程序解析:* 该程序是由两层循环组成的循环嵌套结构。外循环和内循环各自功能不同,相互配合完成了题目要求。* 本程序中用到了如下的一个定义标志的语句:int flag=1;设置素数判断标志,1为素数,0为非素数设置一个标志(flag)是编写程序代码时常用的一种方法,用于控制程序的流程。一般情况下,flag只有两个值1或0,标志两种状态。由于C语言中没有布尔类型的数据,可将 flag定义为int类型。进入循环前将flag值设置为1,当我们希望退出循环时,将flag值设置为0。请读者结合本例中flag的用法,学习并掌握这种编程技巧。注意:(1)3种循环可以互相嵌套,但在循环的嵌套中要注意,内层循环应完全在外层循环里面,也就是不允许出现交叉。(2)如果循环的控制条件永远成立,循环体将永无休止地反复执行,程序就将陷入死循环,这显然是应当防止的。在嵌套的循环结构中,如用缺口矩形表示每层循环结构时,则图3.17中(a)、(b)是正确的多层循环结构,而图3.17(c)是错误的多层循环结构,因为它出现了循环结构的 交叉。
图3.17 多层循环结构示意图3.4 程序结构综合举例按照结构化程序设计的思想,所有程序都可以由3种基本结构组成。而且3种基本结构可以相互多层嵌套,这样就能表示复杂的算法。本章介绍了C语言中用于构造3种基本结构的if语句、switch语句、while语句、dowhile语句和for语句,这些语句之间可以任意嵌套,即if、switch语句中可以有while、dowhile或for语句,而while、dowhile或for语句中也可以有if、switch语句甚至它本身的语句,从而形成复杂的控制结构。下面介绍的这些例子,演示了综合使用控制结构的编程思路及一些经典的算法。另外,还需注意这些例子的实现方法不是唯一的,比如实现循环的例子中既可以用for语句也可以用while语句,请读者用心体会这些例子并多做一些语句变化(将for语句改为while语句等)方面的练习。【例3.19】 输出99乘法表。算法分析:分行与列考虑,共9行9列,i控制行,j控制列。程序如下。#includeint main{int result;计算结果int i,j;fori=1;i外循环控制行:i表示行1~9{forj=1;j内循环环控制列:j表示列(1~i){result=i*j;printf"%d*%d=%-2d ",i,j,result;}printf"\n"; 注意换行格式的控制}return 0;}程序的运行结果如图3.18所示。
图3.18 例3.19的运行结果【例3.20】 求1! 2! 10!算法分析:(1)循环结构中,最常用的算法就是累加和累乘。一般累加和累乘是通过循环结构和循环体内的一句表示累加性或累乘性语句来实现的,如前面多个例子中已运用过的累加器和累乘器。要注意,累加器的初值赋为0,而累乘器的初值赋为1。另外,赋初值的操作一定要在循环结构外进行。(2)本例要用一个双循环来实现,外循环用来求累加,内循环用来求累乘。程序如下。#includeint main{int sum=0,fact=1;说明累加器和累乘器int i=1,j=1;说明两个循环变量whilei外循环求和{fact=1; 注意在内循环外令fact=1j=1;whilej内循环求阶乘{fact=fact*j;j;}sum=sum fact;i;}printf"1! 2! 10!=%d\n",sum;return 0;}程序的运行结果如图3.19所示。
图3.19 例3.20的运行结果【例3.21】 求自然对数e的近似值,要求使其误差小于10 6,近似公式如下。
算法分析:(1)依据近似公式,应该先求第i项的阶乘,再将各项阶乘的倒数进行累加。因此程序中设计两个变量sum和fact分别作为累加器和累乘器。另外,要考虑数据的精度问题。为防止项数过大时阶乘溢出,定义fact为double类型;sum也定义为double类型,是为满足题目的求值精度要求。(2)如何判断循环结束?由于循环次数不确定,所以用while循环好一些。程序如下。#includeint main{double sum=1,fact=1;int i=1;表示第几项while1fact0.0000001 根据题目要求给出计算精度{fact=fact*i;求第i项的阶乘i;sum=sum 1fact;累加}printf"e%lf\n",sum;return 0;}程序的运行结果如图3.20所示。
图3.20 例3.21的运行结果【例3.22】 百钱买百鸡。即假定小鸡每只5角,公鸡每只2元,母鸡每只3元,问:若用100元钱买100只鸡,小鸡、公鸡、母鸡各买多少只?算法分析:(1)这是一个用穷举法解题的典型问题。穷举法也称为枚举法或试凑法,即将可能出现的各种情况一一测试,判断是否满足条件,一般采用循环来实现。(2)列出所有可能的购鸡方案:设母鸡、公鸡、小鸡各为x、y、z只,根据题目要求,列出方程为:x y z = 1003x 2y 0.5z = 1003个未知数,两个方程,此题有若干个解。(3)最简单直接的编程思路是利用三重循环来实现求解过程。程序如下。#includeint main{int x,y,z; x母鸡,y公鸡,z小鸡printf"母鸡个数 公鸡个数 小鸡个数\n";forx=0;x{ fory=0;y{forz=0;z{ ifx y z==100&&3*x 2*y z*0.5==100printf"%-10d%-10d%-10d\n",x,y,z;}}}return 0;}程序的运行结果如图3.21所示。
图3.21 例3.22的运行结果程序解析:分析3个未知数的关系(见下面程序代码中的注释),本例利用两重循环即可实现,提高了程序的效率。程序改写如下。#includeint main{ int x,y,z; printf"母鸡个数 公鸡个数 小鸡个数\n"; forx=0;x100钱都用来买母鸡最多能买33只{ fory=0;y100钱都用来买公鸡最多能买50只{z=100-x-y;if3*x 2*y z*0.5==100 3个未知数的关系printf"%-10d%-10d%-10d\n",x,y,z;}}return 0;}程序运行结果与图3.21相同。在程序代码中加入时间函数,对方法1(三重循环)和方法2(双重循环)进行运行时间的比较,从而了解提高程序的运行效率的意义。为了让两种方法的程序运行时间上的差距大一些而便于比较,将原题目的百钱买百鸡改为千钱买千鸡。方法1程序改写如下,方法2程序改写类似,请读者自己完成。程序如下。#include#include 时间和日期函数需要的头文件#define N 1000定义一个符号常量,方便代码调试修改int main{int x,y,z;time_t t1, t2;time_t是中定义的与时间有关的类型double t2_t1;表示两个时间之间的间隔时间t1=clock; clock是库函数中的计时函数,t1表示循环开始时间for x=0;xfor y=0;yforz=0;z if x y z==N && 3*x 2*y 0.5*z==N printf"%5d%5d%5d\n",x,y,z;t2=clock;t2表示循环结束时间t2_t1=doublet2-t1CLOCKS_PER_SEC;CLOCKS_PER_SE是在time.h文件中定义的常量,表示一秒钟会有多少个时钟计时单元printf"此方法用去%f秒的时间\n",t2_t1;return 0;}方法1程序改写之后的运行结果如图3.22所示,方法2程序改写之后的运行结果如图3.23所示。需要说明的一点是:本例在不同的机器上运行所显示的时间可能是不同的。图3.22 方法1加入时间函数的运行结果图3.23 方法2加入时间函数的运行结果【例3.23】 求Fibonacci数列:1,1,2,3,5,8,的前40个数。该数列的生成方法为:F1=1 n=1,F2=1 n=2,Fn=Fn?1 Fn?2 n3,即从第3个数开始,每个数等于前两个数之和。算法分析:(1)这是一个用迭代法解题的典型问题。迭代法也称辗转法,是一种不断地利用变量的旧值递推新值的过程。利用迭代算法解决问题,需要做好以下3个方面的工作:① 确定迭代变量。在可以用迭代算法解决的问题中,至少存在一个直接或间接地不断由旧值递推出新值的变量,这个变量就是迭代变量。② 建立迭代关系式。所谓迭代关系式,指如何从变量的前一个值推出其下一个值的公式(或关系)。迭代关系式的建立是解决迭代问题的关键,通常可以使用递推或倒推的方法来完成。③ 对迭代过程进行控制。在什么时候结束迭代过程?这是编写迭代程序必须考虑的问题。不能让迭代过程无休止地重复执行下去。(2)本例中,根据Fibonacci数列的特点,至少需要定义两个变量f1和f2分别用来保存Fn?1和Fn?2。可以直接将f1 f2赋给f1,这时f1就是新数列值,这时再把f1 f2赋给f2,f2则是下一个新的数列值。也就是说,如果循环体这样设计:f1=f1 f2;f2=f1 f2;则一次可求出两个数列项的值。程序如下。#includeint main{int f1=1, f2=1;定义并初始化数列的头两个数int i;fori=1; i每次循环控制输出两个数,40个数需20次循环{printf"%12ld%12ld",f1,f2; 输出当前的两个数ifi%2==0printf"\n";输出4个数后控制换行f1=f1 f2;计算新的f1f2=f2 f1;计算新的f2}return 0;}程序的运行结果如图3.24所示。
图3.24 例3.23的运行结果习题1.任意输入3个整数,按由小到大的顺序输出。2.求解在100~200之间,既能被5整除又能被6整除的所有的数。3.输入一行字符,分别统计出其中英文字母、空格、数字和其他字符的个数。4.求圆周率的近似值,要求最后一项的绝对值小于10?8,近似公式如下。 5.对下列数学式求值,要求至少求出前20项(n20)或最后一项的值小于10?3。 6.求出所有的水仙花数。水仙花数是指一个三位数,其各位数字的立方和恰好等于该数本身。例如,153=1*1*1 5*5*5 3*3*3,所以153是水仙花数。依题意,从100~999循环查找水仙花数即可。7.编一程序,将任意自然数n的立方表示为n个连续的奇数之和。例如: 13=1 23=5 3=8 33=11 9 7=27 43=19 17 15 13=648.输入年份和月份,打印出该年份该月份的天数。根据历法,凡是1、3、5、7、8、10、12月,每月31天,凡是4、6、9、11月,每月30天,2月份闰年29天,平年28天。据此,题中采用多个case可共同使用一个语句块的switch语句,完成不同天数的选择。另外,判断某年是否为闰年的规则是:如果此年份能被400整除,则是闰年;否则,如果此年份能被4整除,而不能被100整除,则是闰年;否则不是闰年。9.编程输出以下菱形图案。(1)基本菱形图案,固定图形行数,如图3.25(a)所示。(2)可变行数的菱形图案,从键盘输入行数n,如图3.25(b)所示。(3)增加编程难度,将*改变为字母,菱形图案行数固定,如图3.25(c)所示。(4)在(3)的基础上,使菱形图案的行数可变,从键盘输入图案中心字母,如图3.25(d)所示。 (a) (b)(c) (d)图3.25 习题9的运行结果??
??
??
??
C语言程序设计教程与项目实训
科 3种程序控制结构
|
|