描述
开 本: 16开纸 张: 胶版纸包 装: 平装-胶订是否套装: 否国际标准书号ISBN: 9787302466758
第1章 绪论… 1
1.1 C语言简介… 1
1.2 开发C语言程序… 4
1.2.1 个C语言例程… 4
1.2.2 在Microsoft Windows下开发程序… 8
1.2.3 在Linux或Unix下开发程序… 12
1.3 小结… 16
1.4 习题… 17
第2章 数据和运算… 18
2.1 标识符和关键字… 18
2.2 数据类型… 22
2.2.1 有符号整数系列类型和无符号整数系列类型… 23
2.2.2 字符类型… 26
2.2.3 枚举类型… 27
2.2.4 浮点数类型… 28
2.3 变量和字面常量… 39
2.3.1 变量… 39
2.3.2 有符号整数系列类型和无符号整数系列类型字面常量… 43
2.3.3 字符类型字面常量… 45
2.3.4 枚举类型字面常量… 46
2.3.5 浮点数类型字面常量… 47
2.4 数据的输入和输出… 48
2.4.1 函数printf 48
2.4.2 函数scanf和scanf_s 54
2.4.3 字符输入函数getchar和字符输出函数putchar 64
2.5 运算… 66
2.5.1 算术运算… 67
2.5.2 关系运算… 75
2.5.3 逻辑运算… 76
2.5.4 位运算… 76
2.5.5 赋值类运算… 80
2.5.6 条件运算… 81
2.5.7 其他运算… 82
2.6 小结… 83
2.7 习题… 84
第3章 控制结构… 86
3.1 选择结构… 86
3.1.1 if语句和if-else语句… 87
3.1.2 switch语句… 91
3.2 循环结构… 94
3.2.1 for语句… 94
3.2.2 while语句… 96
3.2.3 do-while语句… 98
3.2.4 continue语句… 99
3.2.5 break语句… 101
3.3 小结… 104
3.4 习题… 105
第4章 结构化程序设计… 108
4.1 函数基础… 108
4.1.1 函数定义与调用… 108
4.1.2 形式参数个数可变的函数… 114
4.1.3 主函数main. 117
4.2 多个源程序文件… 123
4.3 函数递归调用… 130
4.4 结构化程序设计实现… 142
4.5 小结… 148
4.6 习题… 148
第5章 静态数组… 150
5.1 一维数组… 151
5.2 多维数组… 163
5.3 字符数组与字符串… 172
5.4 小结… 194
5.5 习题… 195
第6章 结构体和共用体… 196
6.1 结构体(struct)… 196
6.2 共用体(union)… 208
6.3 小结… 215
6.4 习题… 215
第7章 指针… 216
7.1 指针类型与变量… 216
7.2 动态数组… 222
7.3 指针运算… 229
7.4 函数返回值… 230
7.5 单向链表和双向链表… 241
7.6 函数指针与函数自动测试… 270
7.7 小结… 278
7.8 习题… 278
第8章 关键字typedef和const以及预处理命令… 280
8.1 类型别名定义typedef 280
8.2 常量属性const 285
8.3 预处理命令… 288
8.3.1 宏定义#define与取消宏定义#undef 289
8.3.2 条件编译… 295
8.3.3 文件包含#include. 298
8.4 小结… 304
8.5 习题… 304
第9章 文件处理… 306
9.1 文件操作基本框架… 306
9.2 以文本形式读写文件… 316
9.3 以二进制数据流形式读写文件… 324
9.4 文件整体信息与处理… 327
9.5 错误处理… 328
9.6 文件处理程序示例… 330
9.7 小结… 349
9.8 习题… 349
第10章 编程规范、程序调试与测试… 351
10.1 程序编写规范… 352
10.1.1 命名规范… 352
10.1.2 排版规范… 358
10.1.3 语句… 362
10.1.4 文件组织… 369
10.2 程序编译与调试… 376
10.2.1 程序编译与链接… 376
10.2.2 设置断点与查看即时信息… 382
10.2.3 断言assert 389
10.2.4 编写调试日志文件程序… 392
10.3 程序测试… 396
10.4 小结… 434
10.5 习题… 435
第11章 其他常用函数和宏… 437
11.1 数学库… 437
11.1.1 指数与对数类函数… 437
11.1.2 三角函数… 446
11.1.3 反三角函数… 449
11.1.4 双曲函数… 452
11.1.5 其他数学类函数… 454
11.2 标准库… 458
11.2.1 伪随机数生成函数… 458
11.2.2 程序退出函数以及注册函数… 459
11.2.3 环境变量值的获取函数… 462
11.2.4 查找与排序函数… 463
11.2.5 整数值和除法运算函数… 467
11.3 时间获取与处理函数… 469
11.3.1 在时间库中的时间获取函数… 469
11.3.2 在时间库中的时间处理函数… 471
11.3.3 在时间库中的时间格式输出函数… 476
11.3.4 在VC平台下的高精度计时函数… 481
11.4 小结… 487
11.5 习题… 487
附录A 图的索引…………………………………………………………………………………………. 490
附录B 表的索引…………………………………………………………………………………………. 492
附录C 例程索引…………………………………………………………………………………………. 494
附录D 函数、运算符与宏索引………………………………………………………………………. 496
参考文献……………………………………………………………………………………………………… 502
II C程序设计 III前言
图3-1 if语句和if-else语句流程图 在流程图中,小圆圈表示流程图片断的连接点,即上面流程图连接入其他流程图组成更大流程图的连接点;菱形表示条件判断,而且将依据判断结果执行不同的分支;矩形表示正常的代码执行;箭头表示程序运行的路径。 下面给出if语句代码示例。 int studentScore = 95; if (studentScore>90) printf(“成绩优秀!n”); // 结果输出: 成绩优秀!↙// 1// 2// 3 在上面第2行代码中,因为studentScore = 95,所以if条件表达式“studentScore>90”成立。这样,上面第3行代码if分支语句“printf(“成绩优秀!n”);”就会被执行,结果输出“成绩优秀!↙”。如果将上面第1行代码换为“int studentScore = 85;”,那么if条件表达式“studentScore>90”不成立,这样上面第3行代码就不会被执行,结果什么也没有被输出。 下面给出另外一个if语句代码示例。 int a = 10; int b = 5; if (a>b) { printf(“a=%dn”, a); // 结果输出: a=10↙ printf(“b=%dn”, b); // 结果输出: b=5↙ printf(“a比b大。n”); // 结果输出: a比b大。↙ } // if结束// 1// 2// 3// 4// 5// 6// 7// 8 在这个示例当中,if分支部分是一个语句块。通过语句块使得if分支部分可以包含多条 语句。 : 在if分支语句块当中,作为语句块标志的一对大括号“{ }”是不能去掉的。如果去掉,则if分支语句块就变为if分支语句,而且这条if分支语句就是原语句块的条语句。 例如,在上面的代码示例中,如果去掉其中第4行和第8行代码,则在第3行之后的代码变为 if (a>b) printf(“a=%dn”, a); // 只有当(a>b),才会输出a的值 printf(“b=%dn”, b); // 无论a是否大于b,均会输出b的值 printf(“a比b大。n”); // 无论a是否大于b,均会输出: a比b大。↙
这时,后两行代码“printf(“b=%dn”, b);”和“printf(“a比b大。n”);”并不隶属于if语句。因此,无论a是否大于b,均会输出b的值以及“a比b大。↙”。这显然是有问题的。 if-else语句包含两个分支。if-else语句的格式是: if (表达式) 语句1或语句块1 else 语句2或语句块2 其中,表达式称为if条件表达式,必须是数值类型的表达式,可以是定点数,也可以是浮点数。if下方的语句1或语句块1称为if分支语句或语句块,else下方的语句2或语句块2称为else分支语句或语句块。这里的语句1和语句2均只能是单条语句,语句块1和语句块2也只能是一个语句块。如图3-1(b)所示,只有当if条件表达式不等于0时,才会执行if分支语句或语句块;否则,执行else分支语句或语句块。下面给出if-else语句代码示例。 int studentScore = 85; if (studentScore>=60) printf(“通过考试!n”); else printf(“考试没通过,请继续努力!n”);// 1// 2// 3// 4// 5上面代码示例将输出“通过考试!↙”。 在if语句和if-else语句当中的分支语句仍然可以是if语句或if-else语句。但这时,需要注意if和else在同一个语句块中的近配对原则。 : if和else的近配对原则:在同一个语句块中,else部分总是按照if-else语句格式与近的未配对的if部分配对,构成if-else语句。如果else部分无法与if部分配对构成符合if-else语句格式的语句,那么将出现编译错误。根据这一原则,如果在if-else语句当中的if分支语句或语句块仍然是一条if语句,那么该if语句代码的编写应当采用语句块的形式;否则,该if语句将与else部分配对成为if-else语句。下面给出具体的示例代码进行说明。 这里给出示例代码,说明if和else近配对原则可能出现的问题,以及如何避免可能出现的错误。具体的代码如下。 int month = 12; int day = 30; if (month==12) { if (day==31) printf(“这是一年的后一天!n”); } else printf(“这不是一年的后一个月!n”);// 1// 2// 3// 4// 5// 6// 7// 8// 9 虽然第5行的if比第3行的if离第8行的else更近一些,但第5行的if与第8行的else不在同一个语句块中。因此,上面代码第8行的else只会与第3行的if相配对,而不会与第5行的if相配对。上面代码第4~7行是一个语句块,只包含一条if语句。在if-else语句格式中,允许其中的if分支语句或语句块是一条语句。那么,能否去掉第4行和第7行代码,即去掉作为语句块标志的一对大括号“{ }”? 在去掉这两行代码之后,语法仍然是正确的。这时,上面的代码变为 int month = 12; int day = 30; if (month==12) if (day==31) printf(“这是一年的后一天!n”); else printf(“这不是一年的后一个月!n”);// 1// 2// 3// 5// 6// 8// 9 运行上面的代码,将会输出“这不是一年的后一个月!↙”。为什么会这样? 为什么12月份会不是一年的后一个月? 我们分析一下修改之后的代码。根据if和else近配对原则,在修改之后的代码中,上面代码第8行的else会与第5行的if相配对。这样,修改之后的代码实际上等价于 int month = 12; int day = 30; if (month==12) { if (day==31) printf(“这是一年的后一天!n”); else printf(“这不是一年的后一个月!n”); }// 1// 2// 3// 4// 5// 6// 7// 8// 9 这样,对于12月份,只要日期不是31,就会输出“这不是一年的后一个月!↙”。对于其他月份,反而什么都不会输出。将“if (day==31) printf(“这是一年的后一天!n”);”部分按语句块的形式编写,就不会出现这种与预期不相符的逻辑。总之,在if-else语句当中的if分支语句或语句块仍然是一条if语句的正确语句格式是: if (表达式1) { if (表达式2) 语句1或语句块1 } else 语句2或语句块2 如果if-else语句的else分支语句仍然是一条if-else语句,则通常写成 if (表达式1) 语句1或语句块1 else if (表达式2) 语句2或语句块2 else 语句3或语句块3 这个过程可以有限次重复下去,形成如下的语句格式: if (表达式1) 语句1或语句块1 else if (表达式2) 语句2或语句块2 …… else if (表达式n) 语句n或语句块n else 语句(n 1)或语句块(n 1) 下面给出代码示例。 int studentScore = 85; if (studentScore>=90) printf(“成绩优秀!n”); else if (studentScore>=80) printf(“成绩良好!n”); else if (studentScore>=70) printf(“成绩中等!n”); else if (studentScore>=60) printf(“成绩合格!n”); else printf(“考试没通过,请继续努力!n”);// 1// 2// 3// 4// 5// 6// 7// 8// 9// 10// 11 上面的代码对成绩进行分类。如果分数大于或等于90分,则成绩优秀;如果分数介于80和89之间,则成绩良好;如果分数介于70和79之间,则成绩中等;如果分数介于60和69之间,则成绩合格;如果分数低于60,则考试没通过,需要继续努力。3.1.2 switch语句 switch语句也常称为分支语句。switch语句的格式如下。 switch (表达式) { case 常数1: 语句组1 case 常数2: 语句组2 …… case 常数n: 语句组n default: 语句组(n 1) } 上面switch语句行的表达式称为switch表达式,它必须是整数系列类型的表达式。在switch语句的一对大括号“{ }”内是一系列的case分支和一个default分支。每个case分支在关键字case和空格之后紧接着一个常数,这个常数的数据类型必须与switch表达式相匹配,称为case常数。在同一条switch语句当中,各个case常数必须各不相等;否则,无法通过编译。在case常数之后是冒号,然后是case分支语句组。default分支在同一条switch语句中多出现一次,也可以不出现。在default分支中的语句组称为default分支语句组。case分支语句组和default分支语句组均由一条或多条语句组成,而且每个case分支语句组和default分支语句的后一条语句通常是break语句。如果在这些分支语句组的中间出现break语句,那么在break语句之后的语句实际上将不会起作用。当然,这些分支语句组也可以不含break语句。 如图3-2所示,在执行switch语句时,首先计算switch表达式的值,然后依次将该表达式的值与各个case常数进行匹配。如果该表达式的值刚好等于某个case常数,则进入该case分支,执行相应的case分支语句组。如果该case分支语句组不含break语句,则会继续执行下一个case分支或default分支的语句组,直到执行到break语句或整个switch语句结束。如果switch表达式的值与任何一个case常数都不相等,并且switch语句含有default分支,则执行default分支语句组。
图3-2 switch语句流程图 下面给出一个switch语句示例。 char grade = ‘A’; switch(grade) { case ‘A’: printf(“百分制成绩: 90~100。n”); break; case ‘B’: printf(“百分制成绩: 80~89。n”); break; case ‘C’: printf(“百分制成绩: 60~79。n”); break; case ‘D’: printf(“百分制成绩: 0~59。n”); break; default: printf(“无效成绩。n”); } // switch结束// 1// 2// 3// 4// 5// 6// 7// 8// 9// 10// 11// 12// 13// 14// 15// 16// 17// 18在这个示例中,因为变量grade的值是’A’,所以程序会进入case ‘A’分支,执行该case分支语句组,输出“百分制成绩: 90~100。↙”。因为该case分支语句组的后一条语句是break语句,如上面第6行代码所示,所以switch语句运行到这里就自动结束了。我们还可以修改上面第1行代码,改变grade的值,从而执行switch语句的不同case分支或执行default分支的语句组。 下面给出一个不含break语句的switch语句应用示例。 int month = 5; int dayRemain = 0; switch(month) { case 1: dayRemain = 31; case 2: dayRemain = 28; case 3: dayRemain = 31; case 4: dayRemain = 30; case 5: dayRemain = 31; case 6: dayRemain = 30; case 7: dayRemain = 31; case 8: dayRemain = 31; case 9: dayRemain = 30; case 10: dayRemain = 31; case 11: dayRemain = 30; case 12: dayRemain = 31; } // switch结束 printf(“距离年终还剩余%d天。n”, dayRemain);// 1// 2// 3// 4// 5// 6// 7// 8// 9// 10// 11// 12// 13// 14// 15// 16// 17// 18// 19// 20// 21// 22// 23// 24// 25// 26// 27// 28// 29// 30 这个示例假设某一年2月份总共是28天,希望统计从month这个月开始到年终还剩余多少天。在这个示例中,因为变量month的值是5,所以程序会进入case 5分支,执行该case分支语句组,统计5月份的天数,使得dayRemain= 31。因为该case分支语句组不含break语句,所以程序会进入该switch语句的下一个case分支,即case 6分支,继续统计6月份的天数,使得dayRemain=31 30=61。这个过程不断继续下去,直到后一个case分支,即case 12分支,程序统计了后一个月的天数,该switch语句才执行结束。这时,程序已经统计了从5月份到12月份的总天数,得到dayRemain=245。因此,运行上面第30行代码将输出“距离年终还剩余245天。”。 :(1)在switch语句中,switch表达式必须是定点数类型的表达式。 (2)在switch语句中,case常数必须是定点数类型的常数,不能是浮点数类型的常数。 (3)在同一条switch语句当中,所允许的case分支的总个数总是有限的。C语言标准规定,在同一条switch语句中,case分支的个数不能超过1023。不过,实际所允许的case分支个数依赖于C语言支撑平台。3.2 循环结构 循环结构非常适合发挥计算机的强大运算能力。循环结构的特点是不断重复执行位于在循环体内的程序代码,直到不满足循环条件。有限性是正常计算机程序的基本特点。因此,对于正常的循环结构,应当设计合理的循环条件使得循环终能够在有限的步骤之后结束。循环结构包括for语句、while语句和do-while语句。下面分别介绍这些循环语句。3.2.1 for语句 for语句是C语言的3种循环语句之一。for语句的格式是 for (初始化表达式; 条件表达式; 更新表达式) 循环体 其中,循环体一般是一条语句或一个语句块。 如图3-3所示,在执行for语句时,初始化表达式只会被计算一次。初始化表达式通常用来初始化循环所需要的变量,因此通常由一个或多个赋值运算表达式组成。如果是多个赋值运算表达式,则采用逗号分隔开。
图3-3 for语句流程图 下面给出采用for语句实现计算从1到100之和的示例。 int i, n, sum; for (i=1, n=100, sum=0; i<=n; i ) sum =i; printf(“sum=%d。n”, sum); // 结果输出: sum=5050。↙// 1// 2// 3// 4在上面的示例中,初始化表达式是“i=1, n=100, sum=0”,由3个赋值运算表达式组成,相邻的赋值运算表达式采用逗号分隔。因此,这个初始化表达式实际上就是一个逗号运算表达式。 : 初始化表达式还可以为空。下面给出相应的代码示例。 int n = 100; int sum = 0; int i = 1; for ( ; i<=n; i ) sum =i; printf(“sum=%d。n”, sum); // 结果输出: sum=5050。↙// 1// 2// 3// 4// 5// 6 在上面的示例中,初始化for循环所需要的各个变量的工作已经由第1~3行代码完成。因此,for语句的初始化表达式为空。 : 在C语言的for语句中,通常不允许在初始化表达式中定义变量。下面给出相应的代码示例。 int n = 100; int sum = 0; for (int i=1; i<=n; i ) // 其中“int i=1”无法通过编译 sum =i; printf(“sum=%d。n”, sum);// 1// 2// 3// 4// 5 上面第3行代码通常无法通过编译,不能在初始化表达式中定义变量i。 : 不过,有些C语言支撑平台遵循C 语法规则,可以编译通过上面的代码。但是,为了保证C语言代码的通用性或者说可移植性,仍然不建议在for语句的初始化表达式中定义变量。 如图3-3所示,在计算初始化表达式之后,开始计算并判断for语句的条件表达式。如果条件表达式不等于0,则表明条件表达式成立,这时就会执行for语句的循环体。如果条件表达式等于0,则表明条件表达式不成立,这时就会结束for语句的执行。 在执行完一遍for语句的循环体之后,就会计算更新表达式。更新表达式通常用来更新循环涉及的变量。因此,更新表达式通常是自增或自减或赋值类运算表达式。如果需要在更新表达式中改变多个变量的值,则通常采用逗号运算表达式,即用逗号分隔多个自增或自减或赋值类运算表达式。 : 允许更新表达式为空。如果需要更新循环涉及的变量的值,还可以将更新表达式改写为语句放入循环体内部。下面给出相应的代码示例。 int i, n, sum; for (i=1, n=100, sum=0; i<=n; ) // 更新表达式为空 { // for循环体开始 sum =i; i ; // 更新表达式变成为在循环体中的更新语句 } // for循环体结束 printf(“sum=%d。n”, sum); // 结果输出: sum=5050。↙// 1// 2// 3// 4// 5// 6// 7 在上面代码中,第2行for语句的更新表达式为空。变量i的更新是循环体的后一条语句,位于上面代码的第5行。这种写法是允许的,只是没有原来的简洁。不过,当更新表达式比较复杂时,可以考虑采用这种方式编写代码。 如图3-3所示,在计算更新表达式之后,又会重新开始计算并判断for语句的条件表达式。如果条件表达式成立,则会继续执行for语句的循环体;否则,就会结束for语句的执行。这个过程会不断重复下去,直到条件表达式不成立或者在执行循环体时遇到了break语句。break语句和continue语句将在后面的章节进行讲解。 : 编写循环语句的两个常见问题如下。 (1)整个循环是否得到正确的初始化。因为for语句具有显式的初始化表达式,所以采用for语句出现这种问题的情况比较少。 (2)对于for语句,应当注意更新表达式与条件表达式,既要保证循环体的正常执行,又要保证终会终止循环。 采用for语句的常见场景是要求重复执行循环体n遍。下面给出实现这一目标的两种常见写法。种写法如下。 int n = 100; int i; for (i=1; i<=n; i ) 循环体 // 这里的循环体需要换成实际可行的代码才可以通过编译// 1// 2// 3// 4 如上面第3行代码所示,这种写法变量i从1开始计数,因此条件表达式采用“<=”运算。上面计算从1到100之和的示例非常适合于这种写法。另外一种写法如下。 int n = 100; int i; for (i=0; i图3-4 while语句流程图 从while语句的执行过程可以看出,通常应当在while语句之前完成while语句的循环初始化。而对循环变量的更新则应当在while语句的循环体中完成,从而保证终会终止循环。可以根据这个思想,将for语句改写成为while语句。 下面给出采用while语句实现计算从1到100之和的示例。 int sum = 0; int n = 100; int i = 1; while (i<=n) { sum =i; i ; } // while结束 printf(“sum=%d。n”, sum); // 结果输出: sum=5050。↙// 1// 2// 3// 4// 5// 6// 7// 8// 9 在上面的示例中,对while语句循环的初始化在while语句之前的第1~3行就已经完成。在while语句的循环体中,也就是第7行代码处,实现对while语句的循环变量的更新。读者可以自行比较上面的代码与3.2.1小节采用for语句实现的示例代码。 : 在上面的示例代码中,第5行和第8行作为语句块标志的一对大括号“{ }”是不能去掉的。如果去掉第5行和第8行的代码,则while语句的循环体从一个语句块变为一条语句“sum =i;”。这样,在执行while语句时,变量i的值一直都不会发生变化,从而造成while条件表达式“i<=n”永远成立,程序进入死循环,即无法正常终止while语句的运行。因为while语句的运行无法正常结束,所以无法正常运行到第7行代码“i ;”。 3.2.3 do-while语句 do-while语句是C语言的3种循环语句之一。do-while语句的格式是: do 循环体 while (条件表达式); 其中,循环体一般是一条语句或一个语句块。 : 在do-while语句当中,“while (条件表达式)”的后面紧接着分号,表明do-while语句结束。在关键字do的后面没有分号,除非循环体是空语句。在常规情况下,do-while语句的循环体不会是空语句,因为一个正常的循环通常至少应当具有可以引起循环结束的语句。 如图3-5所示,在执行do-while语句时,先直接执行循环体,再计算并判断条件表达式。如果条件表达式不等于0,则表明条件表达式成立,这时就继续执行do-while语句的循环体。如果条件表达式等于0,则表明条件表达式不成立,这时就会结束do-while语句的执行。在执行do-while语句的循环体之后,又会重新开始计算并判断do-while语句的条件表达式。如果条件表达式成立,则会继续执行do-while语句的循环体;否则,就会结束do-while语句的执行。这个过程会不断重复下去,直到条件表达式不成立或者在执行循环体时遇到了break语句。break语句和continue语句将在后面的章节进行讲解。
图3-5 do-while语句流程图 : 如图35所示,在执行do-while语句时,循环体至少会被执行一遍。 从do-while语句的执行过程可以看出,通常应当在do-while语句之前完成do-while语句的循环初始化。而对循环变量的更新则通常应当在do-while语句的循环体中完成,从而保证终会终止循环。可以根据这个思想,将for语句改写成为do-while语句。 下面给出采用do-while语句实现计算从1到100之和的示例。 int sum = 0; int n = 100; int i = 1; do { sum =i; i ; } while (i<=n); printf(“sum=%d。n”, sum); // 结果输出: sum=5050。↙// 1// 2// 3// 4// 5// 6// 7// 8// 9// 10 在上面的示例中,对do-while语句循环的初始化在do-while语句之前的第1~3行就已经完成。在do-while语句的循环体中,也就是第7行代码处,实现对do-while语句的循环变量的更新。读者可以自行比较上面的代码与3.2.2小节采用while语句和第3.2.1小节采用for语句实现的示例代码。3.2.4 continue语句 C语言标准规定continue语句只能用在循环语句中。continue语句的写法如下。 continue; 如图3-6所示,当执行循环语句遇到continue语句时,程序会自动结束循环体剩余代码的运行。然后,对于for语句,则会立即计算更新表达式,并依据条件表达式,决定是重新继续执行一遍循环体还是结束循环语句;对于while语句和do-while语句,则会立即计算并判断条件表达式,决定是重新继续执行一遍循环体还是结束循环语句。这个过程可以不断地重复下去,直到循环语句运行结束。 : continue语句通常作为条件语句if语句或if-else语句的一部分出现在循环语句的循环体中。如果直接将continue语句作为一条独立的语句放入循环语句的循环体中,则在continue语句之后的循环体语句都将不起作用。 例程3-1 接受输入5个整数并计算其中正整数的平均值例程。 例程功能描述:该例程依次接受5个整数的输入,统计并输出其中正整数的平均值。 例程解题思路:设计整数类型的变量i和n,用来控制输入整数的个数以及循环运行的总次数。设计整数类型的变量number,用来保存输入的整数。设计整数类型的变量sum,用来保存正整数之和;并用整数类型的变量k统计正整数的个数。利用continue语句,跳过对负整数和0的统计。后输出正整数的平均值。在计算平均值时,将整数运算转化成为双精度浮点数运算,提高计算精度。例程由一个源程序文件C_PositiveNumberAverage.c组成,具体的程序代码如下。
图3-6 包含continue语句的循环语句流程图// 文件名: C_PositiveNumberAverage.c;开发者: 雍俊海行号#include #include
int main(int argc, char* args[ ]){ int i, k, number, sum, n; for (i=1, k=0, sum=0, n=5; i<=n; i ) { printf(“请输入第%d个整数: “, i); scanf(“%d”, &number); // 在VC平台中,应将scanf改为scanf_s if (number<=0) // 跳过0和负整数 continue; sum = number; // 对正整数求和 k ; // 统计正整数个数 } // for循环结束 if (k>0) printf(“正整数的平均值是%g。n”, sum/(double)k); else printf(“没有输入正整数。n”); system(“pause”); // 暂停住控制台窗口 return 0; // 返回0表明程序运行成功} // main函数结束// 1// 2// 3// 4// 5// 6// 7// 8// 9// 10// 11// 12// 13// 14// 15// 16// 17// 18// 19// 20// 21 可以对上面的代码进行编译、链接和运行。下面给出一个运行的结果示例。 请输入第1个整数: 12↙请输入第2个整数: ?1↙请输入第3个整数: 13↙请输入第4个整数: ?2↙请输入第5个整数: 14↙正整数的平均值是13。请按任意键继续… 例程进一步说明:如果去掉第11行的代码,则上面第13行和第14行的代码将都不会起作用。上面第11行和第12行代码的共同作用,使得0和负整数不会进入统计,即当输入的是0或负整数时,上面第13行和第14行的代码都不会被执行。上面第16行代码通过“if (k>0)”使得第17行的运算“sum/(double)k”不会出现除数为0的情况。3.2.5 break语句 C语言标准规定break语句只能用在switch语句和循环语句中。break语句的写法如下。 break; 第3.1.2小节已经介绍了在switch语句中的break语句。因此,这里只介绍在循环语句中的break语句。如图3-7所示,当执行循环语句遇到break语句时,程序会立即自动结束整个循环语句的运行。 : break语句通常作为条件语句if语句或if-else语句的一部分出现在循环语句的循环体中。如果直接将break语句作为一条独立的语句放入循环语句的循环体中,则在break语句之后的循环体语句将都不起作用。 例程3-2 接受以0或负整数为结束标志的多个正整数输入,并计算其中正整数的平均值例程。 例程功能描述:该例程依次接受整数的输入。若输入的是正整数,则继续输入;若输入的是0或负整数,则表示输入结束。对于输入的所有正整数,统计并输出这些正整数的平均值。 例程解题思路:设计整数类型的变量n,用来统计输入的正整数的总个数。设计整数类型的变量number,用来保存输入的整数。设计整数类型的变量sum,用来保存正整数之和。因为需要接受多个整数的输入,所以需要采用循环语句。这里采用for循环语句。因为无法提前知道输入的正整数的总个数,所以for语句的条件表达式为1,即这个for语句并不通过for语句条件表达式来结束循环,而是利用break语句结束循环。在for语句的循环体内,接受整数的输入。如果输入的是正整数,则统计已经输入的正整数的总个数,并计算已经输入的正整数的和。一旦发现输入的是0或负整数,则调用break语句,立即结束for循环语句。在结束for语句之后,变量n的值已经是输入的正整数的总个数,变量sum已经是输入的所有正整数之和。因此,这时可以计算平均值。在计算平均值时,可以考虑将整数运算转化成为双精度浮点数运算,以提高计算精度。例程由一个源程序文件C_MultiplePositiveNumberAverage.c组成,具体的程序代码如下。
图3-7 包含break语句的循环语句流程图 // 文件名: C_MultiplePositiveNumberAverage.c;开发者: 雍俊海行号#include #include
int main(int argc, char* args[ ]){ int n, sum, number; for (n=0, sum=0; 1;) { printf(“请输入第%d个整数: “, (n 1)); scanf_s(“%d”, &number); if (number<=0) // 0或负整数表示输入结束 break; sum = number; // 对正整数求和 n ; // 统计正整数个数 } // for循环结束 if (n>0) printf(“正整数的平均值是%g。n”, sum/(double)n); else printf(“没有输入正整数。n”); system(“pause”); // 暂停住控制台窗口 return 0; // 返回0表明程序运行成功} // main函数结束// 1// 2// 3// 4// 5// 6// 7// 8// 9// 10// 11// 12// 13// 14// 15// 16// 17// 18// 19// 20// 21 可以对上面的代码进行编译、链接和运行。下面给出一个运行的结果示例。 请输入第1个整数: 5↙请输入第2个整数: 6↙请输入第3个整数: 7↙请输入第4个整数: 8↙请输入第5个整数: -1↙正整数的平均值是6.5。请按任意键继续… 例程进一步说明: 如果去掉第11行的代码,则上面第13行和第14行的代码将都不会起作用。上面第11行和第12行代码的共同作用,使得一旦发现输入的是0或负整数,则立即结束for循环语句。上面第16行代码通过“if (n>0)”使得第17行的运算“sum/(double)n”不会出现除数为0的情况。 : 如果出现多重嵌套的循环语句,即在循环语句的循环体内仍然含有循环语句,则当遇到break语句时,只是立即结束该break语句所在的那一层的循环语句,而不会结束其外层的循环语句(当然,这个前提是存在外层的循环语句)。图3-8给出了break语句在两重嵌套循环语句中的运行示例。 如图3-8所示,在两重嵌套循环语句中,如果break语句出现在外层循环语句中,则一旦运行到这条break语句,则立即会结束这两重嵌套循环语句的运行;如果break语句出现在内层循环语句中,则一旦运行到这条break语句,只是结束内层循环语句的运行,然后继续执行在外层循环体内并且在内层循环语句之后的语句,即整个外层的循环语句仍然会继续运行。下面给出程序片断示例。 int i, k; for (i=0; i<3; i ) { for (k=0; k<5; k ) { printf(“%d”, k); if (k==1) break; } // 内层for循环结束 } // 外层for循环结束// 1// 2// 3// 4// 5// 6// 7// 8// 9// 10
图3-8 包含break语句的两重嵌套循环语句这个程序片断的运行结果是: 010101 在上面的程序片断中,因为break语句在内层for循环语句中,所以这条break语句不会影响外层的for循环语句。因此,对于以变量i为计数器的外层的for循环语句,它的循环体将会执行3次。这样,从第4行到第9行的内层for循环语句也就会执行3次。对于内层for循环语句,当k为0和k为1时均会输出k的值;而且当k为1时,在输出k的值之后会运行break语句,从而造成立即结束内层for循环语句的运行。因此,每次执行内层for循环语句实际上只是输出“01”。因为内层for循环语句执行3遍,所以终的输出是“010101”。3.3 小结 C语言的3类控制结构各有特点。选择语句包括if语句、if-else语句和switch语句,这些语句使得程序可以根据不同的条件执行不同的语句。循环语句包括for语句、while语句和do-while语句,这三者之间可以互相转换。通常采用哪种语句编写代码简洁,就采用哪种。其中,常用的是for语句,因为它在形式上符合循环的特征。continue语句和break语句在一定程度上起到辅助的作用。通常要慎重使用continue语句和break语句。 : C语言语句的结束标志有可能是分号“;”,也有可能是语句块。3.4 习题 习题3.1 简述C语言有哪些控制结构。 习题3.2 选择语句包括哪些类型的语句? 习题3.3 循环语句包括哪些类型的语句? 它们的区别是什么? 习题3.4 请判断下面各个结论的对错。 (1)在for语句中,初始化表达式和更新表达式均允许为空。 (2)在C语言的各种循环语句中,采用for语句的运行效率是的,因此for语句也是常用的。 习题3.5 请写出下面程序片断输出的内容,并指出下面程序排版的不合理之处。 int x=-1; int y=0; if (x>=0) if (x>0) y=1; else y=-1; printf(“x=%d, y=%d”, x, y);// 1// 2// 3// 4// 5// 6 习题3.6 下面程序片断是否含有语法错误?如果没有,请写出其运行结果的输出 内容。 char ch=’B’; switch (ch) { case ‘A’: case ‘a’: printf(“优秀”); break; case ‘B’: case ‘b’: printf(“良”); default: printf(“再接再厉”); }// 1// 2// 3// 4// 5// 6// 7// 8// 9// 10// 11// 12// 13 习题3.7 下面程序片断是否含有错误?如果没有,请写出其运行结果的输出内容。 double a=6; switch(a) { case 6: printf(“Saturday。n”); break; case 7: printf(“Sunday。n”); break; default: printf(“Unknown。n”); break; } // switch语句结束// 1// 2// 3// 4// 5// 6// 7// 8// 9// 10// 11// 12// 13 习题3.8 下面程序片断是否含有错误?如果没有,请写出其运行结果的输出内容。 int a = 6; switch(a) { case 6.0: printf(“Saturday。n”); break; case 7.0: printf(“Sunday。n”); break; default: printf(“Unknown。n”); break; } // switch语句结束// 1// 2// 3// 4// 5// 6// 7// 8// 9// 10// 11// 12// 13 习题3.9 请找出并更正下面程序片断的错误。 char ch=0; char sum=0; while (ch<5); { ch ; sum =ch; } printf(“sum=%d”, sum);// 1// 2// 3// 4// 5// 6// 7// 8// 9 习题3.10 请总结break语句的用法。 习题3.11 请总结continue语句的用法。 思考题3.12 能否写出不含分号的C语言语句? 思考题3.13 能否写出不以分号结尾的C语言语句? 习题3.14 请编写程序,接受输入10个整数,计算并输出其平均数。 习题3.15 请编写程序,接受输入1个正整数,计算并输出不超过这个正整数的所有“水仙花数”。这里“水仙花数”是一个正整数,它的各个十进制位的立方和等于它本身。例如,1是“水仙花数”,因为1=13。再如,153是“水仙花数”,因为153=13 53 33。 习题3.16 请编写程序,接受一系列整数的输入,其中输入的后一个整数是0。要求计算并输出除了整数0之外其他输入的整数的值和小值。 习题3.17 请编写程序,接受3个正整数y、m和d的输入。请判断y年m月d日是否是一个合法日期。如果是一个合法的日期,则输出这一天是星期几;否则,请输出字符串“这是一个无效的日期”。 ?? ?? ?? ?? 106 C程序设计 107第3章 控制结构
评论
还没有评论。