c 语言程序设计 - abook.cn · 2011 年1 月第 一 版 2014 年8 月第 二 版 2014 年8...

68
普通高等教育计算机系列规划教材 C 语言程序设计 (第二版) 姜海涛 主编 曹震中 刘红娟 叶永凯 副主编 副主编 科学出版社 职教技术出版中心 www.abook.cn

Upload: others

Post on 21-May-2020

0 views

Category:

Documents


0 download

TRANSCRIPT

普通高等教育计算机系列规划教材

C 语言程序设计 (第二版)

姜海涛 主编

闫 超 曹震中 武 楠 王 妍

闫 超 刘红娟 叶永凯 卫 娜

副主编

北 京

副主编 闫 超

学出版社

职教技术出版中心

www.abook.cn

内 容 简 介

本书根据全国计算机等级考试二级 C 语言程序设计考试大纲要求,结

合目前高等院校学生学习计算机程序设计课程的情况组织内容,全面地介

绍了 C 语言程序设计的基础知识,系统地讲述了 C 语言程序设计的基本方

法和技巧。 本书以 ANSI C 语言标准为依据,深入浅出地介绍了 C 语言的基本数

据类型、控制结构、数组、指针、结构体、文件、输入/输出等内容。在讲

解语法规则的同时,结合具体实例讨论了用 C 语言解决实际问题的方法和

技巧,使学生对 C 语言程序的开发过程有整体的了解,掌握基本的计算机

程序设计方法,培养学生利用计算机分析问题和解决问题的能力。本书共

分为 10 章,每章的后面都提供了习题,并附有参考答案。 本书可作为高等院校计算机普及教材,也可作为计算机等级考试辅

导教材。

图书在版编目(CIP)数据 C 语言程序设计/姜海涛主编. —2 版. —北京:科学出版社,2014

(普通高等教育计算机系列规划教材) ISBN 978-7-03-041624-7

Ⅰ. ①C… Ⅱ. ①姜… Ⅲ. ①C 语言-程序设计-高等学校-教材

Ⅳ. ①TP312

中国版本图书馆 CIP 数据核字(2014)第 186317 号

责任编辑:李太铼 / 责任校对:柏连海 责任印制:吕春珉 / 封面设计:耕者设计工作室

出版

北京东黄城根北街 16 号 邮政编码:100717

http://www.sciencep.com

北京印刷厂印刷 科学出版社发行 各地新华书店经销

* 2011 年 1 月第 一 版2014 年 8 月第 二 版2014 年 8 月第六次印刷

开本:787×1092 1/16 印张:19 1/4 字数:438 000

定价:38.00 元

(如有印装质量问题,我社负责调换<VT03>) 销售部电话 010-62134988 编辑部电话 010-62135763-1012 HP03

版权所有,侵权必究

举报电话:010-64030229;010-64034315;13501151303

前 言

C 语言是一种应用十分广泛的通用程序设计语言。它功能丰富,表达能力强,使用

方便灵活,程序执行效率高,可移植性好。C 语言是学生学习计算机课程的必修语言,

也是非计算机专业计算机等级考试(二级)最为普及的课程之一。 本书由讲授 C 语言程序设计多年、具有丰富教学经验的一线任课教师编写,能够帮

助读者快速掌握用 C 语言编写程序的方法和技巧。本书力求全面介绍 C 语言的各种主

要特性,同时结合具体实例指导读者进行学习。书中大部分实例可以直接运行,而不是

孤立的程序段。通过这些实例,不仅演示了如何有效地使用 C 语言,同时向读者介绍了

一些常用的算法、良好的程序设计风格及结构化程序设计的原则。 本书内容安排合理,讲解简明扼要、通俗易懂,能够使初学者轻松掌握 C 语言语法

规则和程序设计方法,主要内容包括:C 语言基本数据类型、运算符和表达式、输入/输出、基本控制结构、函数、数组、指针、结构体、文件。

本书第一版于 2011 年出版,第 1、2、8 章由闫超编写,第 3、5 章由姜海涛编写,

第 4 章由刘红娟、王妍编写,第 6、7 章由曹震中编写,第 9、10 章由武楠编写,全书

由姜海涛统稿。针对任课老师及学生的反馈意见,编者于 2014 年上半年对本书进行了

修订,修订工作主要由姜海涛、叶永凯、曹震中、卫娜完成。曹宝香教授审阅了全书,

并提出了宝贵意见,在此表示衷心的感谢。 由于时间仓促且编者水平有限,书中不足之处在所难免,恳请广大读者提出宝贵

意见。

学出版社

职教技术出版中心

www.abook.cn

目 录

第 1 章 概述 ················································································································································· 1

1.1 什么是程序 ·································································································································· 2

1.2 程序设计语言 ····························································································································· 2

1.2.1 机器语言 ······························································································································ 3

1.2.2 汇编语言 ······························································································································ 3

1.2.3 高级语言 ······························································································································ 3

1.3 C 语言程序 ·································································································································· 4

1.3.1 注释 ······································································································································ 5

1.3.2 关键字 ·································································································································· 6

1.3.3 预处理命令 ·························································································································· 6

1.3.4 函数 ······································································································································ 6

1.3.5 语句 ······································································································································ 7

1.4 编写和运行 C 程序 ··················································································································· 8

习题 ························································································································································ 12

第 2 章 数据类型······································································································································ 14

2.1 变量 ·············································································································································· 16

2.1.1 整型变量 ···························································································································· 17

2.1.2 实型变量 ···························································································································· 18

2.1.3 字符变量 ···························································································································· 19

2.2 常量 ·············································································································································· 20

2.2.1 整型常量 ···························································································································· 20

2.2.2 浮点型常量 ························································································································ 20

2.2.3 字符常量 ···························································································································· 21

2.2.4 字符串常量 ························································································································ 22

2.3 变量初始化 ································································································································ 23

习题 ························································································································································ 24

第 3 章 数据的使用 ································································································································· 26

3.1 应用实例····································································································································· 27

3.2 输入与输出 ································································································································ 28

3.2.1 字符输出——putchar 函数 ······························································································· 29

3.2.2 字符输入——getchar 函数································································································ 29

C 语言程序设计(第二版) iv

3.2.3 格式化输出——printf 函数······························································································· 30

3.2.4 格式化输入——scanf 函数 ······························································································· 36

3.3 运算符和表达式······················································································································· 42

3.3.1 算术运算符 ························································································································ 43

3.3.2 运算符的优先级和结合性 ································································································ 43

3.3.3 赋值运算符 ························································································································ 45

3.3.4 自增、自减运算符 ············································································································ 46

3.3.5 逗号运算符 ························································································································ 48

3.3.6 sizeof 运算符 ······················································································································ 49

3.3.7 表达式语句 ························································································································ 50

3.4 数据类型转换 ··························································································································· 51

3.4.1 数据类型的隐式转换 ········································································································ 51

3.4.2 强制类型转换运算符 ········································································································ 55

习题 ························································································································································ 56

第 4 章 控制结构······································································································································ 60

4.1 关系运算符和关系表达式 ···································································································· 61

4.1.1 关系运算符 ························································································································ 61

4.1.2 关系表达式 ························································································································ 62

4.2 逻辑运算符和逻辑表达式 ···································································································· 62

4.2.1 逻辑运算符 ························································································································ 62

4.2.2 逻辑表达式 ························································································································ 63

4.3 选择结构····································································································································· 64

4.3.1 if 语句 ································································································································· 64

4.3.2 else 子句 ····························································································································· 66

4.3.3 if 语句嵌套 ························································································································· 67

4.3.4 使用 if 语句应注意的问题 ································································································ 69

4.3.5 条件运算符和条件表达式 ································································································ 72

4.3.6 switch 语句 ························································································································· 73

4.3.7 break 语句··························································································································· 75

4.3.8 应用实例 ···························································································································· 76

4.4 循环结构····································································································································· 78

4.4.1 while 语句··························································································································· 78

4.4.2 do while 语句······················································································································ 79

4.4.3 for 语句······························································································································· 82

4.4.4 使用 break 语句·················································································································· 85

4.4.5 使用 continue 语句············································································································· 86

4.4.6 循环语句嵌套 ···················································································································· 86

学出版社

职教技术出版中心

www.abook.cn

目 录 v

4.4.7 应用实例 ···························································································································· 88

习题 ························································································································································ 91

第 5 章 函数 ··············································································································································· 94

5.1 什么是函数 ································································································································ 95

5.2 函数的定义和调用 ·················································································································· 95

5.2.1 计算两个实数的平均值 ···································································································· 95

5.2.2 显示提示信息 ···················································································································· 96

5.2.3 函数的定义 ························································································································ 97

5.2.4 return 语句 ·························································································································· 98

5.2.5 函数的调用 ························································································································ 98

5.3 函数的声明 ································································································································ 99

5.4 函数的参数传递····················································································································· 101

5.5 递归 ············································································································································ 103

5.5.1 函数的递归调用 ·············································································································· 103

5.5.2 递归的思想 ······················································································································ 104

5.5.3 递归的使用 ······················································································································ 105

5.5.4 求解汉诺塔问题的 C 程序······························································································ 106

5.6 局部变量与全局变量 ··········································································································· 107

5.6.1 程序块 ······························································································································ 107

5.6.2 局部变量 ·························································································································· 108

5.6.3 全局变量 ·························································································································· 109

5.6.4 作用域规则 ······················································································································ 110

5.7 变量的存储类别······················································································································111

5.7.1 变量的性质 ······················································································································ 112

5.7.2 auto 存储类别··················································································································· 112

5.7.3 register 存储类别 ············································································································· 113

5.7.4 static 存储类别 ················································································································· 113

5.7.5 extern 存储类别 ··············································································································· 115

习题 ······················································································································································ 117

第 6 章 数组 ············································································································································· 123

6.1 数组的引入 ······························································································································ 124

6.2 一维数组··································································································································· 125

6.2.1 一维数组定义 ·················································································································· 125

6.2.2 一维数组的元素引用 ······································································································ 126

6.2.3 对数组使用 sizeof 运算符 ······························································································· 127

6.2.4 一维数组的初始化 ·········································································································· 128

C 语言程序设计(第二版) vi

6.2.5 一维数组的排序 ·············································································································· 129

6.3 字符数组与字符串 ················································································································ 135

6.3.1 字符数组 ·························································································································· 135

6.3.2 字符串 ······························································································································ 135

6.3.3 字符串的输入/输出 ········································································································· 136

6.4 二维数组和多维数组 ··········································································································· 138

6.4.1 二维数组的定义 ·············································································································· 138

6.4.2 二维数组的元素引用 ······································································································ 139

6.4.3 二维数组初始化 ·············································································································· 139

6.4.4 二维数组使用举例 ·········································································································· 140

6.5 应用实例··································································································································· 140

习题 ······················································································································································ 143

第 7 章 指针 ············································································································································· 147

7.1 基本概念··································································································································· 148

7.1.1 指针和地址 ······················································································································ 148

7.1.2 定义指针变量 ·················································································································· 148

7.1.3 指针的基本运算 ·············································································································· 150

7.2 指针作为函数参数 ················································································································ 152

7.3 指针与数组 ······························································································································ 156

7.3.1 一维数组与指针 ·············································································································· 156

7.3.2 指针的算术运算 ·············································································································· 159

7.3.3 用指针操作数组 ·············································································································· 162

7.3.4 一维数组(名)作为函数参数 ······················································································ 165

7.3.5 二(多)维数组和指针 ·································································································· 167

7.3.6 二维数组(名)作为函数参数 ······················································································ 171

7.4 指针与字符串 ························································································································· 172

7.4.1 用指针操作字符串 ·········································································································· 172

7.4.2 常用字符串处理函数 ······································································································ 174

7.5 指针数组和指向指针的指针 ····························································································· 178

7.5.1 指针数组 ·························································································································· 178

7.5.2 指向指针的指针 ·············································································································· 182

7.6 指向函数的指针和返回指针的函数 ··············································································· 184

7.6.1 指向函数的指针 ·············································································································· 184

7.6.2 返回指针的函数 ·············································································································· 185

7.7 应用实例··································································································································· 186

习题 ······················································································································································ 188

学出版社

职教技术出版中心

www.abook.cn

目 录 vii

第 8 章 预处理指令 ······························································································································· 194

8.1 宏替换 ······································································································································· 195

8.1.1 简单宏替换 ······················································································································ 195

8.1.2 带参数的宏替换 ·············································································································· 196

8.2 文件包含··································································································································· 200

8.3 条件编译··································································································································· 202

8.3.1 #ifdef 指令 ························································································································ 202

8.3.2 #ifndef 指令 ······················································································································ 204

8.3.3 #if 指令 ····························································································································· 206

习题 ······················································································································································ 207

第 9 章 结构体与共用体······················································································································ 209

9.1 结构体 ······································································································································· 210

9.1.1 结构体类型的定义 ·········································································································· 210

9.1.2 结构体变量的定义 ·········································································································· 211

9.1.3 结构体变量的使用 ·········································································································· 213

9.2 结构体数组 ······························································································································ 214

9.2.1 结构体数组的定义 ·········································································································· 214

9.2.2 结构体数组的初始化 ······································································································ 216

9.3 结构体类型指针····················································································································· 217

9.3.1 指向结构体变量的指针 ·································································································· 218

9.3.2 指向结构体数组的指针 ·································································································· 219

9.4 结构体与函数 ························································································································· 221

9.4.1 结构体变量的成员作函数实参 ······················································································ 221

9.4.2 结构体变量作函数参数 ·································································································· 222

9.4.3 指向结构体的指针作函数参数 ······················································································ 224

9.5 链表 ············································································································································ 225

9.5.1 静态链表 ·························································································································· 226

9.5.2 动态链表 ·························································································································· 227

9.6 共用体 ······································································································································· 238

9.6.1 共用体变量的定义 ·········································································································· 238

9.6.2 共用体变量的使用 ·········································································································· 240

习题 ······················································································································································ 242

第 10 章 文件 ·········································································································································· 247

10.1 文件概述 ································································································································ 248

10.1.1 数据文件的存储形式 ···································································································· 248

C 语言程序设计(第二版) viii

10.1.2 文件类型指针 ················································································································ 249

10.2 文件的打开与关闭·············································································································· 249

10.2.1 文件打开函数 fopen ······································································································ 249

10.2.2 文件关闭函数 fclose······································································································ 250

10.3 文件读/写函数······················································································································ 251

10.3.1 文件读函数 fgetc ··········································································································· 251

10.3.2 文件写函数 fputc ··········································································································· 252

10.3.3 文件读函数 fgets············································································································ 253

10.3.4 文件写函数 fputs ··········································································································· 253

10.3.5 文件读函数 fread ··········································································································· 254

10.3.6 文件写函数 fwrite ·········································································································· 255

10.3.7 文件读函数 fscanf·········································································································· 256

10.3.8 文件写函数 fprintf ········································································································· 257

10.4 文件定位 ································································································································ 257

10.4.1 rewind 函数 ···················································································································· 258

10.4.2 fseek 函数 ······················································································································· 258

10.5 其他常用函数 ······················································································································· 260

10.5.1 feof 函数 ························································································································· 260

10.5.2 ferror 函数 ······················································································································ 260

10.5.3 clearerr 函数 ··················································································································· 261

习题 ······················································································································································ 262

附录一 习题参考答案 ·························································································································· 265

附录二 ASCII 字符集 ··························································································································· 283

附录三 运算符及其优先级表 ············································································································ 286

附录四 常用库函数 ······························································································································· 287

附录五 全国计算机等级考试二级 C 语言程序设计考试大纲··············································· 293

主要参考文献 ············································································································································· 296

学出版社

职教技术出版中心

www.abook.cn

第 1 章 概 述

本章要点 ·程序与程序设计语言;

·C 程序的结构;

·C 程序的开发环境及开发过程。

学习目标 ·了解程序设计语言的发展简史和分类;

·掌握 C 程序的基本结构;

·掌握 C 程序的编辑、编译、链接和执行的过程。

C 语言程序设计(第二版)

2

1.1 什么是程序

程序是为完成某一特定任务而定义的一组指令的序列。我们先来看一个游戏:游戏

中有两个人,其中一个被布蒙上双眼,另一个人是你。场地中混乱地摆上许多啤酒瓶。

游戏任务是由你发号施令,指挥被蒙眼者从场地一端穿越到另一端,其间不允许碰倒任

何一个啤酒瓶。 这里我们就可以明白指令的概念,指令就是一组符号。我们可以用以下方式发出

指令:

向左转

迈左脚

前进半步

右脚跟进

向右转

迈左脚

前进一步

右脚跟进

向右转

跳跃

从广义上讲,这就是为完成穿越障碍而制定的程序。不难看出,只要严格按照我们

发出的指令,所有的人均可穿过障碍,到达目的地。另一方面,程序的执行必须严格按

照指令发出的顺序执行,否则将不能到达目的地。 计算机执行程序的过程与这个游戏类似。我们先来看一下程序在计算机上执行的原

理。计算机硬件只能按部就班地执行指令,计算机要想工作,必须通过执行程序来实现。

在这里,计算机就是指令接收者,而程序就是我们向计算机发送的指令序列。计算机通

过逐条执行程序中定义的计算机能够识别的指令来完成规定的任务。另外,类似于人类

能够理解的指令有限,计算机能够识别的指令也是有限的(比人类能理解的要少很多)。

因此,程序必须由计算机能够识别的指令组成。

1.2 程序设计语言

语言就广义而言,是一套共同采用的沟通符号、表达方式与处理规则。人类沟通所

使用的语言称为自然语言。程序设计语言是程序员与计算机交流的主要工具。程序员采

用某种特定的程序设计语言编写程序,计算机执行程序以完成规定的任务。目前世界上

已知现存的语言有 3000 多种,而程序设计语言的种类也多种多样。从程序设计语言的

发展来看,程序设计语言分为低级语言和高级语言两大类。低级语言又分为机器语言与

……

学出版社

职教技术出版中心

www.abook.cn

第 1 章 概 述

3

汇编语言。

1.2.1 机器语言

机器语言是机器指令的集合。机器指令就是计算机能够直接识别并执行的指令。计

算机的机器指令是一个二进制编码。例如,应用 8086 CPU 完成计算 s=768+12288-1280 的三条机器指令如下:

101100000000000000000011

000001010000000000110000

001011010000000000000101 假如将程序错写成如下形式,请找出错误。

101100000000000000000011

000001010000000000011000

001011010000000000000101 不难看出,用机器语言编写程序非常困难,因为记住这些二进制编码或者找出其中

的错误都是非常困难的事情。

1.2.2 汇编语言

早期的程序员们很快就发现了使用机器语言带来的麻烦,于是汇编语言产生了。汇

编语言是汇编指令的集合。汇编语言与机器语言的区别在于指令的表示方法。机器语言

是面向计算机的语言,由于计算机只能识别二进制形式的指令,因此机器语言均采用了

二进制的形式;而汇编指令则是面向程序员的语言,它采用了类似人类所使用的自然语

言的语法来表示这些指令,从而便于程序员阅读和记忆。例如,将寄存器 BX 的内容传

送到寄存器 AX 的机器指令是“1000100111011000”,而对应的汇编指令则为“mov ax,bx”。很明显,后者更方便程序员的阅读和记忆。需要说明的是,计算机只能识别机

器指令,因此需要将采用汇编语言编写的程序翻译成计算机能够识别的指令序列,这一

工作由称为“汇编程序”的专门程序来完成。

1.2.3 高级语言

汇编指令与机器指令基本上是一一对应,它的执行同机器语言一样受底层硬件平台

的限制。更重要的是,用一条条指令实现一个程序的编写过于繁琐。从最初与计算机交

流的痛苦经历中,人们意识到,应该设计一种这样的语言,它接近于数学语言或人类的

自然语言,同时又不依赖于计算机硬件,编出的程序能在不同体系结构的计算机上执行。 回到上一节提到的游戏,一个动作可能需要两步以上的指令才能完成。如果路线比

较复杂,那么我们可能需要非常长的一个指令序列。在指令接收者能理解的前提下,我

们可以把指令进行精简,上述指令序列可精简如下:

向左迈进半步

C 语言程序设计(第二版)

4

向右迈进一步

跳跃

…… 需要说明的是,我们的精简指令是在指令接收者能够理解的前提下,即指令接收者

能够自动地将精简后的指令转换为上述的单步指令执行。例如,当指令接收者接收到“向

左迈进半步”的指令时,他仍然按照“向左转、迈左脚、前进半步,右脚跟进”的步骤

一步步执行。因此,从本质上来讲,精简指令只是提高了发指令者的效率,而指令接收

者(也是执行者)的执行效率并没有因此而提高。 高级语言是对汇编语言的进一步抽象,它更接近于人类使用的自然语言。例如,求

两个数的最大值的 C 语言代码如下所示:

if (a > b)

max = a;

else

max = b; 不难看出,高级语言更接近于人类的自然语言描述。但需要注意,计算机能识别的

只有机器语言,因此用高级语言编写的程序也需要经过专门的编译器程序翻译成机器指

令才能在计算机上执行。

1.3 C 语言程序

C 语言是目前世界上普遍流行、使用非常广泛的高级程序设计语言之一。鉴于 C 语

言对底层硬件操作方面的优势,C 语言广泛应用于操作系统(如 Windows、Linux、Unix等操作系统)、工业控制等软件的开发;另外,C 语言具有绘图能力强、可移植性好的

特点,并具备很强的数据处理能力,因此也适用于二维、三维图形动画软件(如 3D 游

戏)的开发。 有效学习一门新程序设计语言的唯一途径就是使用它编写程序。对于大多数的初学

者来说,编写的第一个程序几乎都是相同的,即在屏幕上输出以下内容:

Hello, World! 在 C 语言中,我们可以使用下列程序代码来实现:

/*程序 1-1,输出"Hello,World!"的简单 C程序*/

#include <stdio.h>

void main()

{

printf("Hello, World!\n"); /*调用格式化输出函数*/

} 在解释上述代码之前,首先进行如下说明:语言均具有一定的语法规则,程序设计

学出版社

职教技术出版中心

www.abook.cn

第 1 章 概 述

5

语言也不例外。与自然语言不同,程序设计语言的规定更为严格。例如,上述代码中的

“#”、“( )”、“{ }”等均不能省略,printf 语句也不能随意换行,否则程序将无法执行。 需要特别注意的是,C 语言程序严格区分代码的大小写形式。例如,在上述程序代

码中,main 不能写成 MAIN、Main 等形式。 在详细介绍程序的组成要素之前,下面再给出一个比程序 1-1 略复杂的程序。程序 1-2

实现了计算两个整数 12 与 23 和的功能,程序执行后,在运行窗口中显示:

sum=35 以下是完整的程序代码:

/*程序 1-2,计算两个整数之和的简单程序*/

#include <stdio.h>

/*下面是 main函数的定义*/

void main()

{ int a,b,sum;

a=12;

b=23;

sum=add(a,b); /*main函数调用 add函数,由 add函数计算整数的和*/

printf(”sum= %d\n",sum);

}

/*add函数实现求两个整数之和的功能*/

int add(int x,int y)

{ int z;

z=x+y;

return(z);

}

1.3.1 注释

程序中,“/*”和“*/”之间包含的内容属于注释,“/*”表示注释的开始,“*/”表

示注释的结束。注释可以单独占一行,也可以和程序中的其他代码放在一行,并且注释

可以占多行。注释一般分为序言性注释和功能性注释。序言性注释通常放在程序的开始

处,用于说明程序的名称、功能、设计思想、版本、设计者等信息;功能性注释通常放

在程序代码内部,用于说明关键数据、语句、控制结构的含义和作用。 为程序适当增加一些注释是一种良好的程序设计习惯。注释可以提高程序的可读

性,同时便于程序的维护。 注释不影响程序的执行,注释只存在于源程序中。源程序在编译时,编译器会忽略

C 语言程序设计(第二版)

6

注释,生成的目标程序中不包含这些注释。

1.3.2 关键字

上述程序中的 include、void 是 C 语言的关键字,关键字是被 C 语言本身所使用的、

具有特殊含义和功能的词汇,不能被用作其他用途。在后面的章节中,我们会逐渐接触

到 C 语言的其他关键字。 注意:C 语言中的关键字全部使用小写形式。

1.3.3 预处理命令

程序代码中的“#include <stdio.h>”是一个预处理命令。预处理命令均以“#”符号

开始,并且每个预处理命令要独占一行。include 表示命令名,称为“文件包含命令”。

“#include <stdio.h>”用于告诉编译器本程序要将一个叫做“stdio.h”的文件内容包含进

来。“stdio.h”(stdio 即为 standard input output 的缩写)是 C 语言标准函数库中定义的一

个头文件,由于 C 语言中的输入/输出操作均由已在标准函数库中定义的输入/输出函数

来实现,而在 stdio.h 文件中包含了这些输入/输出函数的说明信息。因此,在包含了该

头文件的内容之后,我们便可在程序中直接使用这些输入/输出函数。比如,我们在写文

章时,引用了其他文章的内容,就需要在参考文献里进行说明,“#include <stdio.h>”就

类似于参考文献里的说明。因为在程序中我们使用了 stdio.h 文件内说明的输出函数

printf,所以需要在程序的开头进行说明。

注意:与参考文献不同的是,参考文献列表通常放在文章的最后,而#include 预处

理命令通常放在程序的开头。 程序中经常要使用 C 语言标准函数库中提供的输入/输出函数来完成数据的输入/输

出,因此,一般都带有这样一个预处理命令。

1.3.4 函数

程序 1-1 中的其他代码给出了 main 函数的定义,main 是函数名,可称为主函数。

程序 1-2 中不但包含一个 main 函数,还定义了一个名为 add 的函数。 函数(function)是用来构建 C 语言程序的模块,是 C 语言程序的基本组成单位。

通过使用函数可以降低程序开发的难度,并让程序具有良好的结构。 函数的概念来自于数学。在数学中,假定函数 f 和函数 g 的定义如下:

f(x) = x3

g(x,y)=f(x)+3y+1

其中,f、g 称为函数名,x、y 称为函数的自变量(在程序设计中称为函数的参数)。f(x)、g(x,y)的定义给出了通过自变量计算函数值的方法。另外可以看出,函数 g(x,y)中调用了

学出版社

职教技术出版中心

www.abook.cn

第 1 章 概 述

7

函数 f(x),即在进行 g(x,y)的计算时,x3 的计算交由 f(x)完成。C 语言中的函数与数学中

的函数有相似之处,也包括函数名、参数以及具体操作的定义。 程序 1-1 中 main 函数的定义可以分为两部分,函数首部和函数体。 void main()

{

printf("Hello, World!\n");

} 函数首部依次给出函数类型、函数名称和函数参数定义,参数定义放在函数名后的

一对小括号中。函数体放在一对大括号中,其中可以包含一系列的语句,这些语句给出

了函数执行的操作。 和数学函数不同的是,C 语言的函数可以有确定的计算结果,也可以没有。对于没

有明确计算结果的函数应将其类型指定为 void。另外,C 语言函数可以有参数,也可以

没有参数。对于没有参数的函数,其参数定义可以为空白,但函数名后的一对小括号不

能省略。 程序 1-2 中的 add 函数是一个既有参数,也有返回值的函数。参数 x、y 表示 add

函数需要从外部得到两个整数(这两个整数由 mian 函数在调用 add 函数时提供)。add函数的处理结果就是两个整数之和,因此它的类型指定为 int(int 表示一种整型)。

main 函数是 C 语言程序中的一个特殊函数,每个程序必须而且只能包含一个 main函数,它代表程序运行时的入口。程序运行时,首先找到 main 函数,然后依次执行 main函数中包含的每条语句,直到 main 函数的结束。

每个函数(包括用户自定义函数和系统定义函数)都用于实现某一特定的功能,并

且可以相互调用。调用函数时,只需要使用函数名加上小括号括起来的参数即可。 main 函数可以调用其他函数,从而将一部分工作交给其他函数完成,被调用的函数

执行完成后将返回 main 函数,main 函数继续执行直到程序结束。例如,在程序 1-1 中

main 函数调用 printf 函数完成一行文本的输出。其中 printf 为被调用函数名,小括号

中的“Hello, World!\n”为向该函数提供的参数,即输出的内容。 我们在程序中使用的函数可以分为两类,一类是我们为了实现某个功能自己编写的

函数,通常称为“自定义函数”,程序 1-2 中的 add 函数就属于自定义函数;另一类是由

我们使用的编译器提供的函数库中的函数,通常称为“库函数”,程序 1-1 中所使用的

printf 函数就是一个库函数。

1.3.5 语句

C 语言中的语句是程序执行时向计算机发出的指令,语句给出了计算机要执行的操

作。预处理命令、变量定义等内容不算作语句。语句出现在函数体内,一个函数的执行

过程就是依次执行函数体内语句的过程,这些语句实现了函数的功能。 在程序 1-1 中,main 函数体内只包含一个语句:

函数首部

函数体

C 语言程序设计(第二版)

8

printf ("Hello, World!\n"); 这是一个函数调用语句,该语句执行时,main 函数将调用 printf 函数,参数是一个字

符串“Hello, World!\n”,printf 函数将完成在显示器上输出该字符串,然后返回 main 函数。 语句必须以分号结束。在 C 语言程序中,一个语句可以独占一行,也可以占用多行,

多个语句也可以放在一行中,因此分号是语句结束的唯一标志。 另外顺便解释一下,在上述参数中,\n 表示一个换行符,在输出字符串时遇到\n,

输出内容将换行,从下一行的行首开始后续的输出。

1.4 编写和运行 C 程序

由于计算机的硬件系统能够识别和执行的是机器指令,所以用 C 语言编写的源程序

需要通过编译器的编译转换为采用机器指令描述的目标程序。单个目标程序并不是一个

完整的、可独立执行的程序,接下来通过链接将相关的目标程序组合成一个可以独立运

行的可执行程序。 本书的编程与讨论均基于 Visual C++6.0(简称 VC 6.0)编译系统。VC 6.0 不仅是

一个编译器,而且是一个基于 Windows 操作系统的可视化集成开发环境(integrated development environment,IDE)。我们可以在 VC 6.0 内编辑程序、编译程序、运行程序

以及调试程序。在 VC 6.0 下编写和运行 C 程序的步骤如下。 1)启动和运行 VC 6.0,通过单击【开始】→【程序】→【Microsoft Visual Studio

6.0】→【Microsoft Visual C++6.0】启动。VC 6.0 的界面如图 1.1 所示。

图 1.1 Visual C++ 6.0 程序界面

2)单击【文件】菜单下的【新建】命令,打开“新建”对话框,如图 1.2 所示。 3)单击对话框上方的【文件】标签,如图 1.3 所示。在左侧列表中选择文件类型为

“C++ Source File”,在右侧“文件名”输入框中输入程序的文件名(C 程序源文件的

扩展名为“.c”,因此文件名后不要忘记加扩展名“.c”);在“位置“输入框中给出程序

源文件的存放位置。然后单击“确定”按钮。

学出版社

职教技术出版中心

www.abook.cn

第 1 章 概 述

9

图 1.2 “新建”对话框

图 1.3 “新建”对话框——“文件”标签

4)输入程序、编辑程序。编辑程序完毕后单击“构造”按钮(或按快捷键 F7),如图 1.4 所示。

图 1.4 程序编辑界面

C 语言程序设计(第二版)

10

5)单击“构造”按钮后会弹出提示对话框,如图 1.5 所示。对话框的内容为“This build command requires an active project workspace. Would you like to create a default project workspace?”,意思是说“构造命令需要一个活动的工程,你是否愿意创建一个默

认的工程工作区?”。为了保证程序的正常编译、链接,我们需要选择“是”。

图 1.5 单击“构造”按钮出现的提示对话框

6)随后 VC 6.0 将对源程序进行编译和链接,这时应注意查看窗口下方消息窗口出

现的提示信息。如果程序编译成功,在消息窗口会看到“0 error(s),0 warning(s)”的提

示信息,如图 1.6 所示。如果编译器发现程序中存在错误,在消息窗口中会显示错误的

数量,并给出相应的错误提示。这时我们要根据错误提示信息,推断错误产生的原因,

仔细检查源程序,找到引起错误的代码,将其改正,然后重新进行编译和链接。 警告不代表存在错误,但有可能影响程序的正常运行,因此也需要重视。查看错误

或提示信息可以通过快捷键 F4 快速定位。

图 1.6 编译输出内容

学出版社

职教技术出版中心

www.abook.cn

第 1 章 概 述

11

7)至此,程序的编译和链接成功,生成了相应的可执行文件。通过单击右上方的

“!”按钮(或按 Ctrl+F5 组合键),如图 1.7 所示,可以运行程序以查看结果,程序的

运行界面如图 1.8 所示。

图 1.7 执行程序

图 1.8 程序运行界面

注意:关于工作区与工程。通常情况下,稍复杂的程序往往包含多个文件(如头

文件、图标资源文件等),为了便于组织,VC 6.0 采用了工程的概念。而对于更复杂的

软件,可能包含多个工程,由于它们彼此相关,我们可以把它们放在同一个工作区内。

VC 6.0 下的所有程序的编译与运行均基于工程与工作区进行。因此,如果没有创建工程

和工作区,VC 6.0 会在编译时提示用户,系统会自动创建一个工程(扩展名为.dsp)和

工作区(扩展名为.dsw)。相应地,如果要关闭一个程序,我们不能单击【文件】菜单

中的【关闭】命令(该命令关闭当前文件),而应该单击【文件】菜单的【关闭工作区】

命令。

C 语言程序设计(第二版)

12

习 题

一、选择题 1.以下叙述中错误的是( )。

A.C 语言源程序经编译后生成扩展名为.obj 的目标程序 B.C 程序经过编译、链接步骤之后才能形成一个真正可执行的二进制机器指令

文件 C.用 C 语言编写的程序称为源程序,它以 ASCII 代码形式存放在一个文本文

件中 D.C 语言中的每条可执行语句和非执行语句最终都将被转换成二进制的机器

指令 2.以下叙述中错误的是( )。

A.计算机不能直接执行用 C 语言编写的源程序 B.C 程序经 C 编译程序后,生成扩展名为.obj 的文件是一个二进制文件 C.扩展名为.obj 的文件,经链接程序生成扩展名为.exe 的文件是一个二进制文件 D.扩展名为.obj 和.exe 的二进制文件都可以直接运行

3.对于一个正常运行的 C 程序,以下叙述中正确的是( )。 A.程序的执行总是从 main 函数开始,以 main 函数结束 B.程序的执行总是从程序的第一个函数开始,以 main 函数结束 C.程序的执行总是从 main 函数开始,在程序的最后一个函数中结束 D.程序的执行总是从程序中的第一个函数开始,在程序的最后一个函数中结束

4.C 语言源程序名的扩展名是( )。 A..exe B..c C..obj D..cp

5.计算机能直接执行的程序是( )。 A.源程序 B.目标程序 C.汇编程序 D.可执行程序

6.以下叙述中正确的是( )。 A.C 语言程序将从源程序中第一个函数开始执行 B.可以在程序中由用户指定任意一个函数作为主函数,程序将从此开始执行 C.C 语言规定必须用 main 作为主函数名,程序将从此开始执行,在此结束 D.main 可作为用户标识符,用以命名任意一个函数作为主函数

7.以下叙述中正确的是( )。 A.C 程序中的注释只能出现在程序的开始位置和语句的后面 B.C 程序书写格式严格,要求一行内只能写一个语句 C.C 程序书写格式自由,一个语句可以写在多行上 D.用 C 语言编写的程序只能放在一个程序文件中

学出版社

职教技术出版中心

www.abook.cn

第 1 章 概 述

13

8.以下叙述中正确的是( )。 A.C 程序的基本组成单位是语句 B.C 程序中的每一行只能写一条语句 C.简单 C 语句必须以分号结束 D.C 语句必须在一行内写完

二、编程题 1.输入以下程序代码并尝试进行编译,看出现什么错误提示,思考原因。

#include <stdio.h>

main()

{

/*/*programming*/*/

printf("programming!\n");

} 2.根据本章学习的例子,编写程序输出如下图形。

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

第 2 章 数 据 类 型

本章要点 ·变量与常量;

·整型数据、实型数据、字符型数据;

·字符串;

·变量初始化。

学习目标 ·掌握变量的定义方法,对于给定问题,能够正确地选择合适的数据

类型,以定义程序中所需变量;

·掌握常量的表示方法;

·掌握基本数据类型的定义和使用方法;

·掌握变量初始化的作用和方法。

学出版社

职教技术出版中心

www.abook.cn

第 2 章 数 据 类 型 15

考虑以下四则运算式:

123*456+789 该算式由两部分组成:运算符(*、+)和操作数(123、456、789)。其中运算符

决定了将进行何种运算,或者说如何进行运算;操作数则决定了要对谁进行运算。很明

显,要进行运算,二者缺一不可。程序的执行过程从本质上来说就是数据处理(运算)

的过程。程序的组成与上述算式类似,也是由两部分组成:算法和数据。算法决定了如

何进行运算(即运算的步骤)、进行何种运算;数据则是运算的对象。如编写一个求解

一元二次方程的程序,方程的系数、根就是数据;在第 1 章的第一个程序里,要输出的

内容“Hello World!\n”就是数据。需要指出的是:程序中的数据并不只是数值型的数据,

还包括字符类型、数组类型以及比较复杂的自定义类型的数据。 所有的程序都要先加载到内存中,然后才能执行。因此,程序中的数据在处理前都

要存放在内存中。内存是计算机的记忆部件。考虑下面的例子:请马上说出 1984387242+ 12913131 的答案。相信大部分人都无法马上给出答案。为什么呢?其中 主要的原因就

是操作数过于复杂,我们不能马上记住。我们的大脑能够得出运算结果的前提是能够记

住操作数,之所以我们能够马上给出 12+13 的结果,一方面是由于我们知道加法的运

算规则,另外就是我们的大脑记住了操作数。程序运行时所有

数据要存放在内存中也是基于这个原理。 内存的逻辑结构如图 2.1 所示。内存以字节作为基本的存储

单元,图中每个方格表示一个字节。以 1GB 的内存为例,其中

应该包含 230=1073741824 个字节,也就是其结构由 1073741824个方格组成。不难看出,如果不做任何处理,要想找到内存中

存放的数据是非常困难的。 为了方便地实现对内存中数据的存取,计算机以字节为单

位对每个存储单元进行统一编号。这些编号我们形象地称为“内

存地址”。只要记住数据所在存储单元的地址,我们可以很快地

定位到数据存放的位置。另一方面,由于一个字节存放的数据

有限,有的数据可能需要一个以上的字节。因此,只记住数据

的起始地址是不够的,我们还需要记住数据在几个字节中存放。

C 语言中通过将数据进行分类来实现不同类型的数据占用不同大小的存储空间。C 语言

将数据分为整型、实型、字符型三种基本类型。其中,整型又分为短整型、基本整型和

长整型;实型分为单精度实型和双精度实型。同一种类型的数据在内存中存放时所占的

字节数相同。因此,只要知道数据存放的起始地址和数据的类型,我们就可以非常方便

地对数据进行存取。 本章将主要对 C 语言中提供的三种基本数据类型进行详细讨论。2.1 节介绍变量的

定义和三种基本数据类型的含义,2.2 节介绍三种基本数据类型常量的表示方法以及字

符串常量的存储规则, 后 2.3 节介绍变量初始化的方法。

图 2.1 内存结构示意图

C 语言程序设计(第二版) 16

2.1 变 量

对于数学中定义的函数 f(x)=3x+1,x 称为自变量,其值可以改变;而 3 和 1 则是

固定的,其值不能改变。 程序中的数据也有常量和变量之分。值可以改变的数据称为变量。在函数中,自变

量通过指定不同的值而改变。程序设计中变量值的改变则与内存的存放密切相关。在程

序设计中,变量表示内存中一块固定的存储空间,该存储空间具体占几个字节由变量定

义时指定的变量类型决定。变量的值其实就是变量所表示的存储空间中所存放的内容。

由于存储空间中可以存放不同的值,因此变量的值可以改变。 另外,在定义变量时,我们还要指定变量的名字。通过使用变量名,我们可以找到

变量表示的存储空间,并访问其中存放的变量值。 综上所述,变量有名和值之分。在 C 语言中,变量的名必须是合法的标识符。和其

他高级程序设计语言一样,C 语言中用来对变量、符号常量、函数、数组、类型等对象

命名的有效字符序列称为标识符(identifier)。简单来说,标识符就是一个名字。C 语言

中的标识符必须满足以下三个规定: 1)只能由字母、数字和下划线三种字符组成。 2)第一个字符必须是字母或下画线。 3)不能与关键字(关键字也可以称为保留字、系统标识符)重名。 因此,如 student、sum、_a 皆为合法的标识符,而 2a、#b、-c 均为不合法的标

识符。 【例 2-1】 分析以下标识符的合法性:Main、_0、_int、sizeof。 对于第一个标识符 Main,我们首先想到的是它和 C 语言的主函数名 main 重名,但

需要注意的是,C 语言中的标识符区分大小写,因此 Main 与 main 是两个不同的标识符,

它是合法的标识符。 另外,尽管标识符 main 在 C 语言中并不属于关键字,我们也最好不要将自己定义

的标识符取名为 main。 _0 与_int 均属于以下划线开头的标识符,是合法的标识符。 sizeof 是 C 语言中定义的一个运算符,属于 C 语言中定义的关键字,因此不能将其

作为用户标识符来使用。 在程序编写过程中,标识符的取名 好符合“见名知义”的标准。例如,若某个变

量用来存放几个数的和,我们可以将其取名为 sum。这种命名方式对于复杂程序的编写

尤其重要。试想,如果一个程序中的变量名均以 a、b、c、d、…进行命名,我们能很轻

松地明白每个变量的含义吗? C 语言规定:任何变量在使用之前必须进行定义。编译器将根据变量的定义在内存

中为变量分配相应的存储空间。考虑下面的语句:

学出版社

职教技术出版中心

www.abook.cn

第 2 章 数 据 类 型 17

c = a + b; 在上述语句中,我们分别使用了 a、b、c 三个变量,它们表示三个不同的存储空间,

在执行该语句时,计算机将执行如下操作: 1)从变量 a 表示的存储空间中取出数据。 2)从变量 b 表示的存储空间中取出数据。 3)将取出的数据求和。 4)将两个数据的和存入变量 c 表示的存储空间。 不难看出,对于变量的操作均是对变量所表示的存储空间的操作。如果不给变量分

配相应的存储空间,对变量的操作是没有任何意义的,也是不合法的。另一方面,对于

变量的存取操作必须获取两方面的信息:①变量表示的存储空间的起始地址;②变量表

示的存储空间所占的字节数。前者由变量名可以获取,而后者必须通过指定变量的类型

实现。因此,变量定义的格式如下:

类型标识符 变量名; 这样,上面的三个变量 a、b、c 可以定义如下:

int a , b , c; 下面我们对 C 语言提供的三种基本数据类型进行讨论。

2.1.1 整型变量

整型变量是用来存放整数的。整型的基本类型标识符为 int(integer 的缩写)。类型

标识符的作用是在变量定义时指定变量的类型。根据表示的数据范围的不同,C 语言提

供了三种整型:短整型(short int)、基本整型(int)和长整型(long int)。 整型数据的表示范围是由该类型数据所占的存储空间字节数所决定的。我们都知道

计算机中的所有数据都是采用二进制形式在内存中存放。不考虑符号,一个字节(即 8个二进制位)所能表示的二进制数的范围为 00000000~11111111,转换为十进制数为 0~255。也就是说,一个字节能存放的 大数为 255,大于 255 的数就超出了一个字节所能

表示的范围。 C 语言标准并未具体定义每种整型变量所占的字节数,只是要求 short int 类型的字

节数不能比 int 类型少,int 类型的字节数不能比 long int 类型少。三种整型类型所占的

字节数由编译器决定。不同的编译器规定的字节数可能不同。例如,Turbo C 编译器规定

short int 类型占 2 个字节,int 类型占 2 个字节,long int 类型占 4 个字节;而 Visual C++

6.0 编译器规定 short int 类型占 2 个字节,int 类型占 4 个字节,long int 类型占 4 个字节。

因此,在不同的编译环境中同一类型的变量所能存储的数据的范围可能不同。另一方面,

根据不同的表示需求,三种整型又分别提供有无符号类型和有符号类型。无符号类型只能

表示非负数,而有符号类型既可以表示正数也可以表示负数。使用 Visual C++6.0 编译

器时,六种类型的整型变量能存储的数据范围如表 2.1 所示。

C 语言程序设计(第二版) 18

表 2.1 整型的数据范围

类型名 类型标识符 字节数 表示范围 有符号短整型 [signed] short [int] 2 -32768~32767(即-215~215-1) 有符号基本整型 [signed] int 4 -2147483648~2147483647(即-231~231-1) 有符号长整型 [signed] long [int] 4 -2147483648~2147483647(即-231~231-1) 无符号短整型 unsigned short [int] 2 0~65535(即 0~216-1) 无符号基本整型 unsigned [int] 4 0~4294967293(即 0~232-1) 无符号长整型 unsigned long [int] 4 0~4294967293(即 0~232-1)

其中,类型标识符中,[ ]含义是其包含的内容可以省略,省略的前提是不会引起

混淆。 默认情况下,C 语言的整型变量都是有符号整型,在变量中既可以存放正数,也可

以存放负数。为了告诉编译器变量是无符号类型的,需要把它定义为 unsigned 的类型。

无符号整型变量中只能存放非负整数,主要用于系统编程和底层的、与硬件相关的应用。

在本书中,我们主要使用带符号整型。 如前所述,在 C 语言中任何变量在使用前都要先定义。整型变量的定义如下:

short a; /* 定义一个短整型的变量 a */

int b; /* 定义一个基本整型变量 b */

long c; /* 定义一个有符号的长整型变量 c */

unsigned short d; /* 定义一个无符号的短整型变量 d */

unsigned e; /* 定义一个无符号的基本整型变量 e */

unsigned long f; /* 定义一个无符号的长整型变量 f */ 另外,同类型的变量可以通过一个类型标识符定义,例如:

int a ,b , c; /* 定义三个 int类型的变量 a、b、c */

2.1.2 实型变量

如果需要处理带有小数的数据,变量类型应定义为实型。实型数据又称作浮点型数

据。根据表示范围与精度的不同,浮点类型又分为单精度类型(float)、双精度类型

(double)和长双精度类型(long double)。需要说明的是,浮点类型没有有符号数和无符

号数之分,浮点类型变量既可以存放正数,又可以存放负数。浮点型数据在内存中也是以

二进制编码进行存放的,与整型数据不同的是,浮点型数据是以指数形式(如 1.0001×2-4)

进行存放。无论是单精度类型还是双精度类型,它们的二进制编码都由三部分组成: 1)符号位。0 表示正数,1 表示负数。 2)指数部分。指数部分必须为整数,通常采用二进制补码形式存放。指数部分的

位数决定了浮点型能够表示的数的大小。位数越多,所能表示的数越大。 3)小数部分。通常采用二进制补码形式存放。小数部分的位数决定了浮点型能够

表示的数的精度。位数越多,所能表示的数精度越高,即提供的有效数字位数越多。 浮点数在内存中的逻辑结构如下:

学出版社

职教技术出版中心

www.abook.cn

第 2 章 数 据 类 型 19

符号位 指数部分 小数部分

使用 Visual C++6.0 编译器时,float 类型占四个字节,double 类型占八个字节,long double 类型占十六个字节。不难看出,由于双精度类型的指数部分和小数部分的位数均

多于单精度类型,故其表示数的大小和精度均高于单精度类型。 在实际应用中,具体选择哪种类型取决于对精度和数据范围的要求。当精度的要求

不高时,float 类型是一个合适的选择;当精度的要求较高时,可选择使用 double 类型

或 long double 类型。double 类型可以提供满足大多数程序所需要的精度,long double类型可以提供更高的精度,一般很少用到。

浮点型变量的定义如下:

float a; /*定义一个单精度类型的变量 a*/

double b; /*定义一个双精度类型的变量 b*/

long double c; /*定义一个长双精度类型的变量 c*/

2.1.3 字符变量

由于计算机是以二进制的形式进行存储、运算、识别和处理的。因此,像字母、数

字、标点符号等各种字符也必须按特定的规则转换为二进制编码才能存入计算机。字符

编码实际上就是为每一个字符确定一个对应的整数值(以及它对应的二进制编码)。不

同的字符对应不同的整数值,反之亦然。为了在计算机中方便使用,字符的编码都是从

0 开始,连续排列的。实际上,由于字符(包括拉丁字母)与整数值之间没有什么必然

的联系,某一个字符究竟对应哪个整数完全可以人为地规定。为了保证信息交换的一致

性,人们已经制定了一些字符编码标准,其中以 ASCII(American Standard Code for Information Interchange,美国标准信息交换代码)字符编码标准使用的范围 广泛。在

ASCII 编码中,每个字符的编码存储在一个字节(8 位二进制位)中,规定字节的 高

位为 0,余下的 7 位给出字符的编码,这样就可以给出 128 个编码,用来表示 128 个不

同的字符。在表 2.2 中列出了部分 ASCII 编码值及其对应的字符(本书附录二给出了完

整的 ASCII 字符编码表)。

表 2.2 ASCII 字符编码表

编码值 字符 编码值 字符 0~31 控制符 58~64 符号

32 空格 65~90 大写字母 A~Z 33~47 常用符号 91~96 符号 48~57 数字 0~9 97~122 小写字母 a~z

在 C 语言中字符的操作非常简单,因为 C 语言会按小整数的方式处理字符。毕竟

所有字符都是以二进制的形式进行编码的,我们可以很容易的将这些二进制编码看成是

整数。例如字符’A’的二进制编码为 1000001,该编码就是整数 65 的二进制编码。在 ASCII码中,字符的编码范围是 0000000~1111111,这个范围可以看成是 0~127 这些整数。

C 语言程序设计(第二版) 20

字符变量中存放的是字符的 ASCII 码值。例如,若某字符变量当前存放的数据为字

符'a',那么在内存中其实存放的是'a'的 ASCII 码值 97。由于 ASCII 码需要存储在一个字

节中,因此所有的字符变量均占一个字节的存储空间。 字符变量的定义形式如下:

char a; /*定义一个字符类型的变量 a */

思考 编写如下程序:

main()

{

c = 10;

printf("c=%d\n", c);

} 请查看输出结果,并思考为什么会出现这种结果。

2.2 常 量

在程序运行过程中,其值不可以改变的数据称为常量。由于常量也需要在内存中进

行存储,因此常量也有类型之分。例如,0、-1、3 为整型常量;1.23、2.0 为实型常量;

'a'、'b'为字符常量。常量一般通过字面形式即可辨别其类型,因此上述常量称为字面常

量或直接常量。

2.2.1 整型常量

在 C 语言中,整型常量可以用以下三种形式表示。 1)十进制形式,如 123、-99 等。 2)八进制形式。为了与十进制形式进行区分,C 语言规定所有的八进制常量均以 0

开头,如 0123、027 等。 3)十六进制形式。为了方便区分,C 语言规定所有的十六进制常量均以 0x 开头,

如 0x123、0xff 等。 上述各形式的整型常量在内存中仍以二进制形式进行存放。整型常量也有类型,当

程序中使用整型常量时,如果它属于 int 类型的取值范围,编译器会将该常量作为基本

整型数据来处理,否则作为长整型数据来处理。

2.2.2 浮点型常量

在 C 语言中,浮点型常量可以用以下两种形式表示。 1)十进制小数形式,如 1.03、.123、1.等(注意必须有小数点)。 2)指数形式,如 1.123e3 或 1.123E3 都代表 1.123×103。需要注意的是,在该形式

学出版社

职教技术出版中心

www.abook.cn

第 2 章 数 据 类 型 21

下,字符 e 或 E 前面必须有数字,其后的指数必须为整数(可以是负数)。 浮点型常量在内存中是以二进制指数形式进行存放。浮点型常量默认情况下为

double 类型。 【例 2-2】 请分析以下数值哪些是 C 语言中的合法常量。

-80、-080、-8e1.0、-80.0e -80 是一个十进制常量,而且值为负数,因此是合法的整型常量。 对于-080,根据其前缀为 0 我们可以得出其为八进制常量,但八进制中不可能出

现数码“8”,因此该值不是合法的。 -8e1.0 与-80.0e 属于实型常量的指数形式,但指数形式规定,e 前面必须有数字,

且 e 后面的数必须为整数,因此这两个数值也是不合法的。

2.2.3 字符常量

C 语言中的字符常量是用一对单引号括起来的一个字符,如'A'、'a'、'?'、' '等。在 C语言中,字符数据实际上是作为整数来处理的,也就是说字符常量在内存中是作为一个

整数存放的,该整数就是字符对应的 ASCII 码值。例如,在 ASCII 字符集中,字符'0'的 ASCII 码值为 48,它与整数 0 是完全不同的。如果用字符'0'代替这个与具体字符集有

关的整数值(48),那么,程序就无需关心该字符对应的值,增加了程序的易读性。鉴

于字符常量在内存中以整数形式存放,因此它可以像其他整数一样参与算术运算。 某些特殊字符(如换行符)需要通过字符转义序列来表示。字符转义序列以字符'\'

开头,后面跟上若干个字符,这样看起来是多个字符,但只表示一个字符。例如转义序

列'\n'表示换行符。 C 语言中的常用转义序列如表 2.3 所示。

表 2.3 常用转移序列表

转义序列 含义 \a 响铃符 \b 退格符,将光标移动到前一列 \f 换页符,将光标移动到下一页开头 \n 换行符,将光标移动到下一行开头 \r 回车符,将光标移动到本行开头 \t 横向制表符,将光标移动到下一个制表位 \v 纵向制表符 \\ 反斜杠 \? 问号 \' 单引号 \" 双引号

\ooo 1~3 位八进制形式 ASCII 码所代表的字符 \xhh 1~2 位十六进制形式 ASCII 码所代表的字符

有些转义序列用来表示无法打印的,或者无法从键盘输入的字符,例如,转义序列

\a、\b、\f、\r、\t 和\v 表示了通用的 ASCII 码中的控制字符,转义序列\n 表示了 ASCII

C 语言程序设计(第二版) 22

码中的换行符。 转义序列\\允许字符常量或字符串中包含字符\,例如,'\\'表示一个字符常量,该字

符就是字符\。转义序列\'允许字符常量中包含字符',例如,'\' '表示字符'。转义序列\"允许字符串常量中包含字符",例如,字符串"I say \"good morning\"."中含有两个双引号字

符。转义序列\?很少用到。 表 2.3 中 后两行的转义序列称为数字转义序列,它们可以表示任何字符。其中,

八进制转义序列'\ooo'表示一个八进制形式的 ASCII 码值所对应的字符。例如'\101'代表

八进制形式的 ASCII 码值为 101 的字符'A'。八进制数 101 相当于十进制 65,而'A'的十

进制形式 ASCII 码值正是 65。'\12'或'\012'则可以表示换行符,因为换行符的十进制形式

的 ASCII 码值为 10,转换为八进制正是 12。字符常量'\0'表示 ASCII 码值为 0 的字符,

也就是空字符。我们有时以'\0'的形式代替 0,以强调某些数据的字符属性。 同理,十六进制转义序列'\xhh'表示十六进制形式的 ASCII 码值所对应的字符。例如,

'\x41'代表字符'A',因为十六进制形式的 41 转换为十进制为 65,ASCII 码值为 65 的字

符为'A'。

2.2.4 字符串常量

字符串常量是用一对双引号括起来的 0 个或多个字符组成的字符序列,如"I am a string"、""。双引号不是字符串的一部分,它只用于限定字符串。字符转义序列同样可

以用在字符串中,如"Hello world!\n"。 字符串常量中的字符在内存中依次存放。考虑图 2.2 所示的情况,请回答内存中存

放了几个字符串。 很明显,由于没有相应的界定,我们无法判断内存中存放了几个字符串。为了界定

字符串,字符串在内存中存放时自动添加一个空字符'\0'作为串的结尾(该空字符可称为

字符串的结束标志),如图 2.3 所示。因此,存储字符串的存储空间中的字节数比双引号

中的字符数多一个。

a

b

c

d

e

a

b

c

'\0'

d

e

'\0'

图 2.2 内存示意图 图 2.3 字符串存储示意图

我们应该注意字符常量与仅包含一个字符的字符串之间的区别:'a'与"a"是不同的。

'a'表示一个字符,其值是字母 a 的 ASCII 码值,存储时占用一个字节的存储空间;"a"

学出版社

职教技术出版中心

www.abook.cn

第 2 章 数 据 类 型 23

则是字符串,其中包含一个字符,存储时占用两个字节的存储空间,依次存放字符'a'和结束符'\0'。

【例 2-3】 分析下面哪些不是合法的字符常量。

'C'、"C" 、'\xCC'、'\072' 首先,根据字符常量的定义,所有的字符常量均是以单引号括起来,因此"C"不是

合法的字符常量。'C'是常见的字符常量的形式,不难看出它是合法的字符常量。 对于'\xCC'和'\072',它们以'\'打头,因此是字符转义序列,根据字符转义序列的定义,

'\xCC'表示十六进制形式的 ASCII 码为 CC 的字符,'\072'表示八进制形式的 ASCII 码值

为 072 的字符。因此它们均是合法的。

2.3 变量初始化

变量定义后,由于还没有给变量赋值,因此其值是不确定的。要想保证程序能够正

确运行,变量在使用前应该进行赋值。赋值的方式有以下两种: 1)在变量定义的同时给它指定初始值。 2)在变量使用前进行赋值。 第一种赋值方式我们又称为变量的初始化。其一般形式如下:

类型标识符 变量名 1=初值 1, 变量名 2=初值 2,……;

其中初值可以用和变量同类型的表达式提供,如果初值的类型和变量类型不一致,编译

器会采用赋值运算的规则自动进行类型转换(参考 3.4 节)。 例如:

int a = 2, b = 3;

float a = 1.0, b;

double a, b = 0.25;

char a = 'x';

注意:同时定义多个变量时,可以只对其中部分变量初始化。

思考:

请思考是否能用以下方式进行变量初始化。

int a = b = 3; 如果不能,原因是什么?

C 语言程序设计(第二版) 24

习 题

选择题 1.以下选项中,不能作为合法常量的是( )。

A.1.234e04 B.1.234e0.4 C.1.234e+4 D.1.234e0 2.以下叙述中错误的是( )。

A.用户所定义的标识符允许使用关键字 B.用户所定义的标识符应尽量做到“见名知意” C.用户所定义的标识符必须以字母或下划线开头 D.用户定义的标识符中,大、小写字母代表不同标识符

3.按照 C 语言规定的用户标识符命名规则,不能出现在标识符中的是( )。 A.大写字母 B.连接符 C.数字字符 D.下画线

4.以下选项中不合法的用户标识符是( )。 A.j2_KEY B.Double C.4d D._8_

5.可在 C 程序中用做用户标识符的一组标识符是( )。 A.and _2007 B.Date y-m-d C.Hi Dr.Tom D.case Bigl

6.以下选项中不合法的标识符是( )。 A.print B.FOR C.&a D._00

7.以下选项中合法的标识符是( )。 A.1_1 B.1-1 C._11 D.1__

8.下列定义变量的语句中错误的是( )。 A.int _int; B.double int_; C.char For; D.float US$

9.以下关于 long、int 和 short 类型数据占用内存大小的叙述中正确的是( )。 A.均占四个字节 B.根据数据的大小来决定所占内存的字节数 C.由用户自己定义 D.由 C 语言编译系统决定

10.以下能正确定义且赋初值的语句是( )。 A.int n1=n2=10; B.char c=32; C.float f=f+1.1; D.double x=12.3E2.5;

11.以下不合法的数值常量是( )。 A.011 B.1e1 C.8.0E0.5 D.0xabcd

12.以下选项中不合法的字符常量是( )。 A.'\018' B.'\"' C.'\\' D.'\xcc'

13.以下选项中合法的字符型常量是( )。 A.'\x13' B.'\081' C.'65' D."\n"

学出版社

职教技术出版中心

www.abook.cn

第 2 章 数 据 类 型 25

14.以下选项中,合法的一组 C 语言数值常量是( )。 A.028 5e-3 -0xf B.12. 0Xa23 4.5e0 C.177 4e1.5 Oxabc D.0x8A 10,000 3.e5

15.以下选项中不能作为 C 语言合法常量的是( )。 A.'cd' B.0.1e+6 C."\a" D.'\011'

16.以下选项中能用作数据常量的是( )。 A.o115 B.0118 C.1.5e1.5 D.115L

17.以下选项中正确的定义语句是( )。 A.double a;b; B.double a=b=7; C.double a=7,b=7; D.double,a,b;

18.若函数中有定义语句:int k;,则( )。 A.系统将自动给 k 赋初值 0 B.这时 k 中的值无定义 C.系统将自动给 k 赋初值-1 D.这时 k 中无任何值

第 3 章 数据的使用

本章要点 ·基本输入/输出操作的实现;

·常用运算符(算术运算符、赋值运算符、自增自减运算符、逗号运

算符);

·运算符的优先级和结合性;

·表达式及表达式语句;

·数据类型转换。

学习目标 ·熟练掌握字符的输入/输出函数及格式化输入/输出函数的使用方法;

·重点掌握 scanf 函数和 printf 函数的格式控制;

·掌握常用的运算符和表达式的使用,重点掌握自增和自减运算符及

其运算规则;

·掌握表达式语句的格式,理解表达式与表达式语句的区别;

·掌握数据类型转换方法及规则。

学出版社

职教技术出版中心

www.abook.cn

第 3 章 数据的使用 27

“输入数据—处理数据—输出结果”是程序的典型运行模式。输入数据指的是用户采

用事先设计好的方式,通过输入设备提供程序运行所需的原始数据,程序接收这些输入

数据并将其转换成程序内部各种类型的数据;处理数据指的是程序根据事先设计好的功

能和算法对原始数据进行加工、变换和运算等操作,从而得到用户所需要的目标数据;

输出结果指的是程序采用事先设计好的方式,通过输出设备将目标数据输出。 本章主要讲述 C 语言程序实现数据的输入/输出的基本方法以及使用 C 语言中的运

算符对数据进行处理。 输入/输出功能并不是 C 语言本身的组成部分,而且在计算机系统中可以使用各种类

型的输入/输出设备,如何通过这些设备完成数据的输入/输出本身也是一个比较复杂的

问题。本章 3.2 节主要介绍 C 语言标准函数库提供的用于实现输入/输出的库函数。ANSI C 语言标准精确地定义了这些库函数,在任何可以使用 C 语言的计算机系统中都包含这

些库函数的具体实现。如果程序的输入/输出部分仅仅使用了标准函数库提供的函数,则

可以不经修改地从一个系统移植到另一个系统。 C 语言提供了大量的运算符,使用这些运算符可以方便的表达对数据的各种处理要

求。本章 3.3 节主要介绍一些常用运算符的使用方法,包括算术运算符、自增自减运算

符、赋值运算符和逗号运算符。其他运算符的介绍请参考后续章节的内容。 另外,在 3.1 节给出了一个包含输入/输出及基本运算的程序实例,帮助读者了解本

章要讲述的主要内容。在 3.4 节讨论了各种数据类型的相互转换问题。

3.1 应 用 实 例

下面通过一个实例简单了解运算符的使用和数据的输入/输出。某顾客到超市购买苹

果和橘子,超市收银台计算客户购买商品的总价格。收银员只需输入顾客购买商品的单

价和数量,程序就可以计算出顾客应交款数。 下面给出实现这一功能的程序代码。为了方便读者阅读,同时便于下面的讨论,我

们给程序代码增加了行号。但需要注意,C 程序的代码是不需要行号的。 【例 3-1】 计算商品总价格。 (1)程序代码

/*程序 3-1*/

1 #include <stdio.h>

2 void main()

3 {

4 float apple_price,orange_price,apple_qty,orange_qty,total_price;

6 printf("请输入顾客购买的苹果的价格和数量:");

7 scanf("%f%f",&apple_price,&apple_qty);

8 printf("请输入顾客购买的橘子的价格和数量:");

9 scanf("%f%f",&orange_price,&orange_qty);

C 语言程序设计(第二版) 28

10 total_price=apple_price*apple_qty+orange_price*orange_qty;

11 printf("顾客购买的商品总价格为:%.2f",total_price);

12 } (2)代码分析 这里先简单介绍关键语句的含义,具体的语法格式及使用方法将在本章详细讨论。 第 4 行代码:定义了 5 个 float 类型变量:apple_price、orange_price、apple_qty、

orange_qty、total_price,分别用于存储苹果的价格、数量,橘子的价格、数量及商品总

价格。 第 6、8 行代码:输出语句,通过调用 printf 函数来完成字符串的输出。这里输出的

是提示信息。 第 7、9 行代码:输入语句,通过调用 scanf 函数来完成数据输入。这两个语句分别

读取了苹果和橘子的价格和数量,并存入相应的变量中。 第 10 行代码:利用表达式 apple_price*apple_qty+orange_price*orange_qty 计算商

品总价格,并将结果存入变量 total_price。表达式中用到了乘法运算符(*)和加法运算

符(+)。 第 11 行代码:通过调用 printf 函数输出总价格,即输出变量 total_price 的值。

(3)运行程序 程序开始运行后,将从 main 函数的第一条语句开始执行。运行过程如下。

请输入顾客购买的苹果的价格和数量:1.5 3↙

请输入顾客购买的橘子的价格和数量:1.2 2.5↙

顾客购买的商品总价格为:7.50 其中带底纹的内容为收银员输入的内容(符号↙表示按 Enter 键),其他为程序输出

的内容。可以看出,只要收银员按规定格式输入商品价格和数量,程序就能计算出商品

总价格并输出。

3.2 输入与输出

数据的输入/输出是一个程序必须要考虑的问题。C 语言中数据输入/输出的工作由相

应的库函数来实现。这些库函数由开发工具提供,对于 C 程序的设计者来说,这些库函

数是现成的函数,不需要自己编写,可以直接在程序中调用它们。本节主要介绍四个常

用输入/输出函数:两个实现字符输入/输出的函数——getchar 函数和 putchar 函数;两个

格式化输入/输出函数——scanf 函数和 printf 函数。C 语言要求在调用函数前对被调用的

函数进行声明,调用库函数也应如此,这可以通过包含相应的头文件(head file)来完

成。C 语言的标准函数库被分为 15 部分,每一部分用一个头文件来描述,头文件中包

含了数据类型的定义,宏定义和相关函数的声明。本章介绍的四个函数都在 stdio.h 头文

件中进行描述。因此,我们在程序开头包含 stdio.h 文件即可。方法如下:

#include <stdio.h>

学出版社

职教技术出版中心

www.abook.cn

第 3 章 数据的使用 29

或 #include "stdio.h"

对于这两种形式的区别以及 include 指令的具体用法请参考第八章的相关内容。

3.2.1 字符输出——putchar 函数

使用 putchar 函数可以向标准输出设备(通常指显示器)中输出一个字符。调用方法

如下:

putchar(ch); /*其中参数 ch表示要输出的字符*/ 例如:

putchar('A');

putchar(65);

putchar('\n');

char ch='a'; putchar(ch); 可以看出,参数 ch 可以是字符常量,也可以是字符变量;可以是普通字符,也可以

是字符转义序列。 若参数 ch 为整数,则将该整数转换成字符后输出。 【例 3-2】 使用 putchar 函数输出字符。

/*程序 3-2*/

#include <stdio.h>

void main()

{

putchar('\\');

putchar('\'');

putchar('a');

putchar('\'');

putchar('\\');

}

程序输出结果如下:

\'a'\ 程序中使用了字符转义序列\\和\'。

3.2.2 字符输入——getchar 函数

使用 getchar 函数可以从标准输入设备(通常指键盘)中读取一个字符。调用方法

如下:

char ch;

ch=getchar(); 调用该函数不需要实际参数,该函数的返回值为读取的字符,如果读取出错,返回

C 语言程序设计(第二版) 30

值为 EOF,EOF 是在头文件 stdio.h 中定义的一个符号常量,它表示-1。 【例 3-3】 读取一个字符并输出该字符对应的十进制 ASCII 码值。

/*程序 3-3*/

#include <stdio.h>

void main()

{

char ch;

ch=getchar();

printf("%d",ch);

} 程序运行后,等待我们提供输入数据,假如我们通过键盘输入以下内容:

A↙

然后程序会输出 65,即字符 A 的 ASCII 码值为 65。 我们在通过键盘向程序提供数据时,按 Enter 键表示确认并提交输入数据。此时。

我们所输入的字符(包括回车键,在程序中回车键表示换行符)将依次进入输入缓冲区。

getchar 函数将从输入缓冲区中提取一个字符,将其作为函数返回值。 当我们输入“A↙”时,实际输入了两个字符:字母 A 和换行符。它们依次进入输

入缓冲区。getchar 函数只提取第一个字符 A,而换行符将留在输入缓冲区中。

注意:getchar 函数可以读取空格、Tab 键、换行符等字符。

3.2.3 格式化输出——printf 函数

1.基本语法要求

不同于 putchar 函数每次调用只能输出一个字符,printf 函数每次调用可以输出多个

多种类型的数据,而且可以定义每个数据的输出格式。 【例 3-4】 计算并输出两个整数的和。

/*程序 3-4*/

#include <stdio.h>

void main()

{

int a=1,b=2,sum;

sum=a+b;

printf("sum=%d\n",sum);

} 程序输出结果如下:

sum=3

学出版社

职教技术出版中心

www.abook.cn

第 3 章 数据的使用 31

printf 函数的调用格式如下:

printf(格式字符串,表达式 1,表达式 2……) printf 函数是一个多参数函数。其功能是输出格式字符串的内容,并在格式字符串

中指定位置插入所要输出的数据。其中,第一个参数是一个字符串,它说明了数据的输

出格式和最后的输出效果,这是必须有的一个参数;从第二个参数开始的其他参数给出

了要输出的数据,每个输出数据可由一个表达式给出。

2.格式字符串的构成

格式字符串中包含两种内容。第一种是普通字符(包括字符转义序列),如程序 3-4中的“sum=”,这些内容直接输出。第二种内容是格式说明符,如程序 3-4 中的“%d”,每个格式说明符指定一个输出数据的格式。它起到占位符的作用,格式说明符不被输出,

而是由相应的输出数据来替换它。 格式说明符的格式如下:

% [标志字符] [ 小宽度] [精度] [长度修饰符] 转换说明符

1)格式说明符必须以字符“%”开始,以一个转换说明符结束。常用转换说明符包

括 d、i、o、x、u、c、s、f、e、g。 2)标志字符可以是一个或多个以下字符的组合:“-”、“+”、“#”、“0”。这部分内

容是可选的。 3) 小宽度的形式为 m,m 代表一个十进制整数。这部分内容是可选的。 4)精度的形式为.n,其中 n 代表一个十进制整数。这部分内容是可选的。 5)长度修饰符是以下字符中的一个:“l”、“L”。这部分内容是可选的。

3.转换说明符的含义

转换说明符指定了把数据从内部的二进制形式转换成打印形式的方法。例如,%d指定 printf 函数把 int 型数值从二进制形式转换成由十进制数字字符组成的字符串。下面

详细介绍常用转换说明符的使用方法。 1)转换说明符 d、i 用于输出 int 类型的数据,采用带符号十进制形式表示数据。

/*程序 3-5*/

#include <stdio.h>

void main()

{

int a=123,b=-123;

printf("%d,%d,%d\n",a,b,a+b);

} 程序输出结果如下:

123,-123,0

C 语言程序设计(第二版) 32

2)转换说明符 u、o、x、X 用于输出 unsigned int 类型的数据。采用无符号十进制

(u),无符号八进制(o),无符号十六进制(x 或 X)形式表示数据。字母 abcdef 用于

转换说明符 x,字母 ABCDEF 用于转换说明符 X。

/*程序 3-6*/ #include <stdio.h>

void main()

{

unsigned int a=65535;

printf("%u,%o,%x,%X\n",a,a,a,a);

} 程序输出结果如下:

65535,177777,ffff,FFFF

注意:转换说明符 o、x、X 不输出八进制或十六进制数的前导标识符 0 或 0x。

3)转换说明符 f、e、E、g、G 用于输出 double 类型的数据。 转换说明符 f采用[-]ddd.dddddd的十进制小数形式表示数据,且默认输出 6位小数,

超出部分按四舍五入处理。 转换说明符 e 采用[-]d.dddddde±dd 的十进制指数形式表示数据,且小数点前面有

一位非零数字,小数点后面默认包含 6 位小数,超出部分按四舍五入处理。 转换说明符 g 将根据数值大小和指定精度(默认精度为 6,超出部分按四舍五入处

理)自动选择使用 f 或 e 的输出格式,选择格式 e 仅在转换结果的指数部分小于-4 或

大于等于精度时。简单说就是如果数值适中将选择采用 f 的输出格式,如果数值的绝对

值太大或太小将选择采用 e 的输出格式。转换说明符 g 不输出小数尾部的 0。 字母 e 用于转换说明符 e 或 g,字母 E 用于转换说明符 E 或 G。

/*程序 3-7*/ #include <stdio.h>

void main()

{

double a=1234.567;

printf("%f,%e,%g\n",a,a,a);

} 程序输出结果如下:

1234.567000,1.234567e+03,1234.57

4)转换说明符 c 用于输出 char 类型的数据。

/*程序 3-8*/ #include <stdio.h>

void main()

学出版社

职教技术出版中心

www.abook.cn

第 3 章 数据的使用 33

{

char ch1='A',ch2='\x42';

printf("%c,%c\n",ch1,ch2);

} 程序输出结果如下:

A,B

5)转换说明符 s 用于输出字符串,从字符串的第一个字符开始输出,直到字符串的

结束标志字符,但不输出结束标志字符。

/*程序 3-9*/ #include <stdio.h>

void main()

{

char string[ ]="I like C.";

printf("%s\n","Hello,world.");

printf("%s",string);

}

程序输出结果如下:

Hello,world.

I like C. 程序中的 string 是字符数组(关于字符数组的内容请参考 6.3 节),里面存放了一个

字符串。 在字符串中可以使用转义序列以便包含一些特殊字符。

/*程序 3-10*/ #include <stdio.h>

void main()

{

printf("One\tTwo\tThree\nFour\tFive\tSix\n");

printf("\"Good morning!\"\n");

printf("\\C Language\\");

}

程序输出结果如下:

One Two Three

Four Five Six

"Good morning!"

\C Language\ 使用 printf 函数应注意:

C 语言程序设计(第二版) 34

1)格式字符串中的格式说明符的数量应和输出数据的数量相等。 C 语言编译器不会检测格式字符串中格式说明符的数量是否和输出项的数量相

匹配。

/*程序 3-11*/ #include <stdio.h>

void main()

{

int a=1,b=2,c=3;

printf("%d %d\n",a);

printf("%d\n",b,c);

} 程序 3-11 中,第一个 printf 函数调用所使用的格式说明符多于要显示的值的数量,

printf 函数将正确显示变量 a 的值,接着显示一个无意义的整数值。第二个 printf 函数调

用使用了过少的格式说明符,这种情况下,printf 函数会显示变量 b 的值,但不显示变

量 c 的值。 2)使用正确有效的转换说明符,否则程序将产生无意义的输出结果。

/*程序 3-12*/ #include <stdio.h>

void main()

{

int a=1;

double b=1.2;

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

}

printf 函数会严格按照格式字符串进行输出,所以它将显示出一个 double 型数值,

接着显示一个 int 型数值,可是这两个数值都是无意义的。因为转换说明符 f 和 d 分别

用于输出 double 类型和 int 类型的值,而程序中所要输出的数据不符合这种要求。

4.指定最小宽度和精度

小宽度 m 是一个整数,它指定了显示一个数据所需要的 少字符个数。如果转换

后数据的字符个数比 m 小,输出数据时,在数据的左侧补充适当数量的空格字符,如果

转换后数据的字符个数比 m 大,则 m 不起作用,将按数据的实际字符个数输出。 指定精度用“.n”的格式,其中 n 是一个整数。精度对于不同的转换说明符具有不

同的含义。 对于 d、i、o、u、x 和 X,它给出了转换后数据应包含的 少数字字符数。如果数

字字符数小于 n,则在数据前面添加 0。如果省略.n,则默认精度为 1。 对于 f、e 和 E,它给出了转换后数据的十进制小数部分小数点后的数字字符数。如

果小数点后的数字字符数大于 n,则进行四舍五入处理。如果省略.n,则默认精度为 6。 对于 g 和 G,它给出了转换后数据的 大有效数字字符数。如果省略.n,则默认

学出版社

职教技术出版中心

www.abook.cn

第 3 章 数据的使用 35

大有效数字字符数为 6。 对于 s,它给出了从字符串截取的子串的 大字符数。截取是从字符串的第一个字

符开始的。

/*程序 3-13*/ #include <stdio.h>

void main()

{

int a=1234;

double b=123.4567;

char s[ ]="welcome";

printf("%d,%6d,%2d,%6.5d\n",a,a,a,a);

printf("%f,%11f,%6f,%10.2f\n",b,b,b,b);

printf("%g,%10g,%5g,%10.2g\n",b,b,b,b);

printf("%s,%8s,%.3s,%8.3s\n",s,s,s,s);

} 程序输出结果如下:

1234, 1234,1234, 01234

123.456700, 123.456700,123.456700, 123.46

123.457, 123.457,123.457, 1.2e+002 welcome, welcome,wel, wel

注意:为了便于读者阅读,在上述输出结果中用符号 代表空格字符。在后面

的章节中,必要时也将用符号 代表空格字符。

5.标志字符的含义

常用的标志字符包括“-”、“+”、“#”、“0”,它们的使用顺序是任意的。 标志-:指示转换后的结果在输出域中左对齐(默认为右对齐)。 标志+:指示有符号数转换后的结果带有正号或负号。 标志#:指示对于转换说明符 o,转换结果将增加前缀 0;对于转换说明符 x 或 X,

转换结果将增加前缀 0x 或 0X。 标志 0:指示对于转换说明符 d、i、o、u、x、X、e、E、f、g 和 G,前导零将用于

充填域宽而不充填空格字符。若标志 0 和标志-同时出现,则标志 0 不起作用。

/*程序 3-14*/ #include <stdio.h>

void main()

{

int a=255;

printf("%6d,%-6d,%+d\n",a,a,a);

printf("%o,%#o,%x,%#x\n",a,a,a,a);

printf("%06d,%-06d\n",a,a);

C 语言程序设计(第二版) 36

} 程序输出结果如下:

255,255 ,+255

377,0377,ff,0xff

000255,255

6.长度修饰符的含义

常用的长度修饰符包括:“l”、“L”。 修饰符 l用于输出 long int或 unsigned long int类型的数据,它可用在转换说明符的 d、

i、o、u、x 或 X 前面。 修饰符 L 用于输出 long double 类型的数据,它可用在转换说明符的 f、e、E、g 或

G 前面。

/*程序 3-15*/ #include <stdio.h>

void main()

{

long int a=12345678;

long double f=123.456789;

printf("%ld,%Lf",a,f);

} 程序输出结果如下:

12345678,123.456789

3.2.4 格式化输入——scanf 函数

1.基本语法要求

不同于 getchar 函数每次调用只能读取一个字符,scanf 函数每次调用可以读取多个

多种类型的数据,而且可以定义每个数据的输入格式。 【例 3-5】 读取两个整数并计算它们的和。

/*程序 3-16*/ #include <stdio.h>

void main()

{

int a,b,sum;

scanf("%d%d",&a,&b);

sum=a+b;

printf("sum=%d\n",sum);

} 程序运行后输入:

学出版社

职教技术出版中心

www.abook.cn

第 3 章 数据的使用 37

11 22↙

然后程序输出:

sum=33 scanf 函数将从输入内容中获取两个整数 11、22 并存入变量 a、b。

scanf 函数调用的一般格式如下:

scanf( 格式字符串,地址 1,地址 2……) 其中第一个参数是一个字符串,它指定了 scanf 函数可以接收的输入数据的格式。

scanf 函数将按照格式字符串所描述的输入模式,从用户输入的内容当中获得需要的数

据,然后将这些数据存入相应的变量中。 从第二个参数开始的后续参数给出了用于存放输入数据的存储空间的地址。 为了得到变量的地址,需要使用地址运算符“&”,该运算符是一元运算符。&a、

&b 的含义是得到变量 a、b 的存储空间的地址,这样才符合 scanf 函数对参数的要求。

注意:如果 scanf函数调用中忘记在变量名前面使用&运算符,将会产生不可

预知而且可能是毁灭性的结果。程序崩溃是常见的结果;即使程序没有崩溃,也不会把

输入的数据存放到变量中,此时变量将保持原有的值(如果变量没有赋初值,其值将是

不确定的)。

2.格式字符串的构成

格式字符串中的内容可以分为三类: 1)格式说明符:

% [赋值屏蔽符] [ 大宽度] [长度修饰符] 转换说明符 每个格式说明符给出了一个输入数据的可接受格式。如格式说明符%d 表示可以接受

一个带正负号的十进制整数。格式说明符必须以字符“%”开始,以一个转换说明符结

束。在字符%之后还可以带有可选的赋值屏蔽符、 大宽度和长度修饰符。 2)空白符,包括空格、水平制表符(Tab 键)、换行符等。 3)非空白符,既不是格式说明符也不是空白符的其他字符,如字符','。 类似的,用户输入的内容也可以分为这样三类。

3.scanf 函数的执行过程

下面通过一个程序实例解释 scanf 函数读取数据的过程。

/*程序 3-17*/ #include <stdio.h>

void main()

{

int a,b,sum;

C 语言程序设计(第二版) 38

scanf ("%d,%d",&a,&b);

sum=a+b;

printf ("sum=%d\n",sum);

}

scanf 函数从输入内容中获取数据的过程实际上是一个模式匹配的过程,通过格式字

符串对输入内容进行匹配分组,从而得到每个数据。 假设程序运行时输入:

123,456↙ 然后程序将输出:

sum=579 从输出结果可以看出,scanf 正确地读取了输入的两个整数。 实际上,输入内容就是一个字符串,它将存入输入缓冲区。scanf 函数将依次提取输

入缓冲区中的每个字符,并尝试与格式字符串匹配。匹配将从格式字符串的第一个元素

(可以是一个格式说明符或一个空白符或一个非空白符)开始,到格式字符串的 后一

个元素结束。在这个过程中,如果出现匹配不成功的情形,则 scanf 函数将立即返回。 针对上面的输入内容,其匹配过程如下:

1)转换说明符%d 可以匹配一个带正负号的十进制整数,同时它将忽略数据开始前

的空白符。输入缓冲区中的" 123"将被提取出来,然后%d 和"123"匹配。而输入缓冲

区中的下一个字符','不是构成整数的有效符号,因此它将留在缓冲区中。 2)格式字符串中的字符','需要输入缓冲区中下一个字符必须是相同字符才能匹配成

功。因此输入缓冲区中的字符','将被提取出来。 3)转换说明符%d 将和输入缓冲区中的"456"进行匹配,"456"会从缓冲区提取出来。 4)匹配结束,而换行符'↙'将留在输入缓冲区中。 通过上述匹配过程,就可以从输入内容中获取两个整数 123 和 456。 假设程序运行时输入:

123 456↙ 此时将提示匹配不成功,程序会输出一个无意义的结果。这次匹配的过程如下: 1)转换说明符%d 可以和"123"匹配," 123"将被提取出来。 2)字符','需要和相同字符匹配,而输入缓冲区中下一个字符是空格。匹配失败,scanf

函数会立即返回。 通过上述匹配过程可知,scanf 函数只能从输入内容中获取一个整数 123。所以变量

a 的值是 123,而变量 b 并没有得到一个有效的值。 现在我们再来看本节开头给出的程序 3-13。在程序中调用 scanf 函数时的格式字符

串为"%d%d",其含义是读取两个十进制整数。根据前面了解的 scanf 函数的匹配方法,

以下输入数据的方式都是有效的。

学出版社

职教技术出版中心

www.abook.cn

第 3 章 数据的使用 39

第一种方式:

12 34↙ 第二种方式:

12↙

34↙ 第三种方式:

12 ↙

34↙ 以上几种输入数据方式,都将使 scanf 函数读取整数 12 和 34。而且 scanf 函数读取

数据的过程也基本相同。 1)第一个%d 将匹配第一个整数 12,同时会忽略掉数据开始前的空白符,第一个整

数从字符 1 开始,字符 2 后面的空白符将意味将第一个整数结束。这时"12"或" 12"将从缓冲区提取出来。

2)第二个%d 将匹配第二个整数 34,同时会忽略掉数据开始前的空白符,第二个整

数从字符 3 开始,字符 4 后面的空白符将意味将第二个整数结束。这时" 34"或"↙34"或" ↙34"将从缓冲区提取出来。

下面总结匹配规则,如表 3.1 所示。

表 3.1 scanf 函数的匹配规则

格式字符串中 输入缓冲区中

格式说明符 构成实际数据的字符如%d 匹配"123" 注意,格式说明符可以跳过实际数据开始前的空白符

一个或多个连续的空白符 任意多个连续的空白符 其他字符 相同字符

4.转换说明符的含义

常见的转换说明符有 d、o、u、x、f、e、g、s。其含义如表 3.2 所示。

表 3.2 转换说明符的含义

转换说明符 含 义 d 匹配一个带符号十进制整数,用于读取 int 类型的值 o 匹配一个无符号八进制整数,用于读取 unsigned int 类型的值 u 匹配一个无符号十进制整数,用于读取 unsigned int 类型的值 x 匹配一个无符号十六进制整数,用于读取 unsigned int 类型的值 f、e、g 匹配一个带符号十进制浮点数(小数或指数形式),用于读取 float 类型的值 c 匹配一个字符,不会跳过空白符,用于读取字符数据 s 匹配一个不含空白符的字符串,用于读取字符串数据

程序示例如下。

/*程序 3-18*/

C 语言程序设计(第二版) 40

#include <stdio.h>

void main()

{

int a;

unsigned int b,c,d;

float e,f;

scanf("%d%o%u%x",&a,&b,&c,&d);

scanf("%f%f",&e,&f);

printf("a=%d,b=%d,c=%d,d=%d\n",a,b,c,d);

printf("e=%f,f=%f",e,f);

} 程序运行后输入:

123 123 123 123↙

123.5 1.235e2↙ 然后程序输出:

a=123,b=83,c=123,d=291

e=123.500000,f=123.500000 程序中的%o 将和输入内容中的"123"匹配,并且将 123 解释为一个八进制编码(该

编码转换成十进制即为 83)。同样,%x 会将输入内容解释为一个十六进制编码。使用

scanf 函数读取实数时,转换说明符%f、%e、%g 是可以互换的,它们采用同样的规则

识别一个实数。 转换说明符 c 用于读取一个字符,并且不会忽略空白符。

/*程序 3-19*/ #include <stdio.h>

void main()

{

char a,b;

scanf("%c%c",&a,&b);

printf("%c%c",a,b);

} 程序运行后输入:

a b↙ 然后程序输出:

a 可以看出第一个%c 匹配字符 a,第二个%c 匹配空格字符,而字符 b 和换行符将留

在输入缓冲区中。 下面将上述程序进行简单修改,看一看会出现什么效果。

学出版社

职教技术出版中心

www.abook.cn

第 3 章 数据的使用 41

/*程序 3-19修改版*/

#include <stdio.h>

void main()

{

char a,b;

scanf("%c %c",&a,&b); /*格式字符串中两个%c之间有一个空格字符*/

printf("%c%c",a,b);

} 程序运行后输入:

a b↙ 然后程序输出:

ab 可以看出修改后的程序运行时,第一个%c 匹配字符 a,第二个%c 匹配字符 b。出现

这种结果是因为格式字符串中的空格字符将和输入内容中的空格字符进行匹配。

5.赋值屏蔽符的含义

赋值屏蔽符是字符*,它表示读取数据项,但不会将它赋值给变量。通过它可以跳过

输入内容中的一个数据。

/*程序 3-20*/ #include <stdio.h>

void main()

{

int a,b;

scanf("%d%*d%d",&a,&b);

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

} 程序运行时输入:

11 12 13↙ 然后程序输出:

a=11,b=13 程序中的 scanf 函数将读取三个整数 11、12、13,然后将 11 赋值给变量 a,13 赋值

给变量 b,而整数 12 将被丢弃。

6.最大宽度的含义

可以使用一个十进制整数 m 指定输入数据的 大宽度,它指定了构成一个数据项的

大字符个数,实际数据开始前的空白符不会被统计。

/*程序 3-21*/

C 语言程序设计(第二版) 42

#include <stdio.h>

void main()

{

int a,b;

scanf("%3d%4d",&a,&b);

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

} 程序运行时输入:

12345 6↙ 然后程序输出:

a=123,b=45 程序中%3d 表示第一个整数 多由 3 个字符构成,所以它将匹配"123"(忽略 123 之

前的空格);%4d 表示第二个整数 多由 4 个字符构成,但同时要保证构成数据的字符

是有效的,所以它将匹配"45"。

7.长度修饰符的含义

常用的长度修饰符包括 h、l 和 L。h 用于读取短整型数据,可用在转换说明符 d、o、u、x 之前。l 用于读取长整型数据或 double 类型数据,可用在 d、o、u、x 或 e、f、g之前。L 用于读取 long double 类型数据,可用在 e、f、g 之前。

/*程序 3-22*/ #include <stdio.h>

void main()

{

double d;

scanf("%lf",&d);

printf("d=%f",d);

} 程序运行时输入:

123.456↙ 然后程序输出:

d=123.456000 特别需要注意的是,读取 double 类型数据应使用%lf,而不能使用%f。

3.3 运算符和表达式

C 语言的一个主要特征就是它更多地强调表达式而不是语句。表达式是一个表示如

学出版社

职教技术出版中心

www.abook.cn

第 3 章 数据的使用 43

何计算的公式。运算符是构成表达式的基本工具,C语言中运算符数量之多,在高级语

言中是少见的,正是丰富的运算符使C语言功能十分完善。

3.3.1 算术运算符

C 语言的算术运算符如表 3.3 所示。

表 3.3 算术运算符

一元运算符 二元运算符 乘除类 加减类 正号运算符+

负号运算符- 乘法运算符 * 除法运算符 / 模运算符 %

加法运算符+ 减法运算符-

二元运算符要求有两个操作数,而一元运算符只要求有一个操作数。例如,表达

式-b 使用的是一元运算符,表达式 goods1_price*goods1_qty+goods2_price*goods2_qty使用的是二元运算符。

使用算术运算符时应注意: 1)运算符%表示模运算(mod)或取余运算(rem)。表达式 a%b 的值是 a 除以 b 后

的余数。例如 10%2 的值为 0,10%3 的值为 1。 2)%运算符要求两个操作数必须是整数,其他运算符允许操作数可以是整数或实数。 3)当操作数均是整型时,运算符/的计算结果也是整型(在 C 语言中,算术运算结

果的类型和操作数的类型相同),结果是通过舍去小数部分来得到。所以,1/2 的结果是

0 而不是 0.5,要想使结果为 0.5 可以使用 1.0/2 或 1/2.0 或 1.0/2.0。 4)避免使运算符/和运算符%的第二个操作数为 0。 5)对于运算符/和运算符%,若两个操作数均为正数,计算结果是比较容易确定的。

若操作数中含有负数,计算结果由程序的运行环境决定。例如,对于-10/3 和-10%3,一种处理方式是-10/3 等于-3,-10%3 等于-1;另一种处理方式是-10/3 等于-4,-10%3 等于 2。无论哪种处理方式都应使(a / b)*b+a%b 等于 a。

使用算术运算符将操作数连接起来得到的式子称为算术表达式。算术表达式的值就

是算术运算符的计算结果。

3.3.2 运算符的优先级和结合性

1.关于优先级

考虑表达式 i+j*k,其含义应解释为 i+(j*k),而不应该是(i+j)*k。这是因为

数学中有一个“先进行乘除运算,后进行加减运算”的基本准则,它表明乘除运算的优

先级高于加减运算。在 C 语言中,当表达式中含有多个运算符的时候,也要根据运算符

的优先级对表达式进行解释,这和我们在表达式中使用括号是一样的含义。通常,优先

级高的运算符先进行计算。 C 语言中的运算符优先级共分 15 级,1 级 高,15 级 低。算术运算符的优先级如下:

C 语言程序设计(第二版) 44

+(正号运算)和-(负号运算):2 级; *(乘)、/(除)和 %(模运算):3 级; +(加)和-(减):4 级。

所以: i+j * k 等价于 i+(j*k); -i *-j 等价于(-i)*(-j)。

2.关于结合性

考虑表达式 i-j-k,其含义应解释为(i-j)-k,而不是 i-(j-k)。这是因为数

学中减法运算是从左向右进行结合的。当表达式中含有多个优先级相同的运算符时,运

算符的结合性开始起作用。如果运算符是从左向右结合的,称这种运算符是左结合的。

如果运算符是从右向左结合的,称这种运算符是右结合的。二元算术运算符都是左结合

的,一元算术运算符都是右结合的。所以: i+j+k 等价于(i+j)+k; i-j+k 等价于(i-j)+k。 为了正确地使用运算符,一种做法是记住或查看这些运算符的优先级和结合性规则,

第二种做法就是在表达式中增加足够的括号就可以了。 需要注意的是,除了&&(逻辑与运算符)、||(逻辑或运算符)、?:(条件运算符)

和,(逗号运算符)之外,C 语言没有规定子表达式的计算顺序。例如对于表达式 i * j + x * y,应解释为(i*j)+(x*y),但没有规定 i * j 和 x * y 先计算哪一个。

【例 3-6】 正整数 m 是一个 3 位数,按逆序输出 m 的 3 位数。

/*程序 3-23*/

#include <stdio.h>

void main()

{

int m,a,b,c;

printf("Input m:");

scanf("%d",&m);

a=m%10;

b=m/10%10;

c=m/100;

printf("units digit:%d\n",a);

printf("tens digit:%d\n",b);

printf("hundreds digit:%d\n",c);

} 程序运行后输入:

357↙ 然后程序输出:

学出版社

职教技术出版中心

www.abook.cn

第 3 章 数据的使用 45

units digit:7

tens digit:5

hundreds digit:3 程序中变量 a、b、c 分别用于存放整数 m 的个位数字、十位数字和百位数字。

3.3.3 赋值运算符

程序中的变量为数据存储提供了临时的存储空间,我们可以将数据存入变量,从而

改变变量的值。C 语言提供了赋值运算符实现这种操作。赋值运算符可分为简单赋值运

算符和复合赋值运算符。

1.简单赋值运算符

简单赋值运算符用“=”表示,它是一个二元运算符。由“=”连接左右操作数形

成的表达式称为赋值表达式。例如表达式 a=1+2(假定这里的 a 为 int 型变量)的作用

是计算表达式 1+2 的值,然后将该值赋值给变量 a,即将该值存入变量 a 的存储空间。

此时变量 a 的值为 3。 赋值运算符的左操作数被称为“左值”,右操作数是一个表达式。左值是引用内存中一

个命名的存储单元的表达式。变量是我们目前所知道的左值,而常量和 1+2、j*k、a=1等这样的表达式不是左值。

赋值表达式的值和类型等于赋值后的左操作数的值和类型。例如表达式 a=1+2 的

值为 3,类型为 int。 赋值运算符的优先级是 14,结合方向是自右向左。所以表达式 a=b=c=1,等价于

a=(b=(c=1)),执行过程是,先将 1 赋值给 c,再将 1 赋值给 b, 后将 1 赋值给 a,同时整个赋值表达式的值也为 1。

2.复合赋值运算符

C 语言提供的复合赋值运算符包括+=、-=、*=、/=、%=、<<=、>>=、&=、

^=、|=。它们可以在变量原有值的基础上改变变量的值。例如:

a+=1 等价于 a=a+1; b-=c+1 等价于 b=b-(c+1); i*=j/k 等价于 i=i*(j/k)。 复合赋值运算符的优先级同样是 14,结合方向也是自右向左。例如表达式 a*=a+=

2,等价于 a*=(a+=2),也即等价于 a=a*(a=a+2)。如果变量 a 的原值为 1,赋值运算

后变量 a 的值为 9。 【例 3-7】 读取两个整数存入变量 a、b,然后交换变量的值。

/*程序 3-24*/

#include <stdio.h>

void main()

C 语言程序设计(第二版) 46

{

int a,b,t;

printf("Input a and b:");

scanf("%d%d",&a,&b);

printf("Before exchange:a=%d,b=%d\n",a,b);

t=a; a=b; b=t;

printf("Aefore exchange:a=%d,b=%d\n",a,b);

} 程序运行后输入:

3 5↙ 然后程序输出:

Before exchange:a=3,b=5

Aefore exchange:a=5,b=3

程序 3-24 中,通过依次执行 3 条赋值语句完成了变量 a、b 值的交换。其中变量 t是一个辅助变量。

如果不借助第三个变量,同样能实现交换变量 a、b 值的功能。程序如下: /*程序 3-25*/

#include <stdio.h>

void main()

{

int a,b,t;

printf("Input a and b:");

scanf("%d%d",&a,&b);

printf("Before exchange:a=%d,b=%d\n",a,b);

a=a+b; b=a-b; a=a-b;

printf("Aefore exchange:a=%d,b=%d\n",a,b);

}

3.3.4 自增、自减运算符

在编程中,尤其是在循环结构中,我们经常要让变量的值加 1 或减 1。这类要求可

以使用赋值运算完成,如 a=a+1。同时,C 语言也提供了两个简洁的自增、自减运算

符来实现这种操作。 自增、自减运算符分别用“++”和“――”来表示。运算符“++”可以使变量

的值加 1,运算符“――”可以使变量的值减 1。例如,表达式 a++运算后变量 a 的值

将加 1。 自增、自减运算符是一元运算符,既可以作为前缀运算符,也可以作为后缀运算符。

例如,++a、――a 使用前缀运算符,a++、a――使用后缀运算符。这两种形式都可

以使得变量的值加 1 或减 1。它们的区别是:前缀自增、自减运算先使变量的值加 1 或

减 1,然后在表达式中引用变量的值;后缀自增、自减运算先在表达式中引用变量的值,

学出版社

职教技术出版中心

www.abook.cn

第 3 章 数据的使用 47

然后使变量的值加 1 或减 1。例如: 1)假设 a=1,执行 b=++a 后,a 的值为 2,而且表达式++a 的值也为 2,变量

b 被赋值为 2。 2)假设 a=1,执行 b=a++后,a 的值为 2,而表达式 a++的值为 1,变量 b 被

赋值为 1。 后缀自增、自减运算符的优先级是 1 级,结合方向是自左向右;前缀自增、自减运算

符的优先级是 2 级,结合方向是自右向左。例如,表达式 j=-i++等价于 j=-(i++)。

下面的程序演示了自增、自减运算符的特性。 /*程序 3-26*/

#include <stdio.h>

void main()

{

int a=1,b=5,t;

printf("%d,",a++);

printf("%d\n",a);

printf("%d,",++a);

printf("%d\n",a);

printf("%d,",b--);

printf("%d\n",b);

printf("%d,",--b);

printf("%d\n",b);

} 程序输出结果如下:

1,2

3,3

5,4

3,3

使用自增、自减运算符时应注意: 1)如果仅需要使变量 i 的值加 1 或减 1,而不需要在表达式中引用变量的值,则 i

++和++i 的作用是一样的。否则,需要对这两种形式严格区分。 2)尽量避免在一个表达式中过多的使用自增、自减运算符,尤其是类似于(i++)

+(i++)的表达式。因为这会降低程序的可读性和可移植性,而且有可能产生歧义,

在不同的运行环境中产生不同的结果。 3)自增、自减运算符的操作数应是左值。变量是左值,而常量和 i+j、i++、+

+i 等表达式不是左值,所以表达式 1++、(i+j)++、(i++)++、++(++i)等都是错误的。

自增、自减运算符通常用于整型变量或指针变量,特别是在循环结构中应用比较普

遍。本书第 4 章、第 6 章、第 7 章将介绍自增、自减运算符的使用。

C 语言程序设计(第二版) 48

3.3.5 逗号运算符

C 语言提供逗号运算符用于连接两个表达式。逗号运算符用符号“,”表示。形如“左

表达式,右表达式”的式子称为“逗号表达式”。由逗号运算符连接的两个表达式从左

向右依次计算。先计算左表达式的值,并且丢弃该值,再计算右表达式的值。逗号表达

式的值和类型取右表达式的值和类型。例如,表达式 1+2,3+4 的值为 7。 逗号运算符的优先级 低。例如,i=1*2,3*4 等价于(i=1*2),(3*4)。 先计算左表达式 i=1*2,使得 i 被赋值为 2,然后计算右表达式 3*4 得结果 12,逗

号表达式的值也为 12。 使用逗号运算符应注意: 1)逗号运算符明确规定了子表达式从左向右计算。例如,假设 i=1,表达式“a=i,

i++”的运算顺序是变量 a 先被赋值为 1,然后变量 i 自增 1。 2)逗号运算符会丢弃左表达式的值,但不会忽略左表达式的计算。例如,表达式

“a=1,b=2”将使变量 a 被赋值为 1,变量 b 被赋值为 2;表达式“i++,j++”将

使变量 i、j 的值加 1。 3)注意逗号运算符和作为标点符号的逗号的区别。例如: 假设,a=1,b=2,c=3,语句

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

执行后输出结果如下:

1 2 3

其中的逗号是标点符号,用于分割函数的三个实际参数。 语句

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

执行后输出结果如下:

1 3 3

其中,(b,c)是一个逗号表达式,它表示一个要被输出的数据,其值为 3。 通常,我们使用逗号运算符只是为了连接多个表达式,并让这些表达式依次被计算,

很少会使用逗号表达式本身的值。逗号表达式经常用于 for 语句,详细介绍参见第 4 章。 【例 3-8】 使用逗号运算符。

/*程序 3-27*/

#include <stdio.h>

void main()

{

int a,b,c;

a=1,b=2,c=3;

printf("a=%d,b=%d,c=%d\n",a,b,c);

学出版社

职教技术出版中心

www.abook.cn

第 3 章 数据的使用 49

a++,b++,c--;

printf("a=%d,b=%d,c=%d",a,b,c);

} 程序输出结果如下:

a=1,b=2,c=3;

a=2,b=3,c=2

3.3.6 sizeof 运算符

在程序中使用 sizeof 运算符可以确定存储指定类型的值所需要的存储空间的大小。

sizeof 运算符用法如下:

sizeof 操作数

其中的操作数可以是一个表达式,或加括号的类型名。例如:

sizeof(int)

sizeof(a+b) 表达式的运算结果就是操作数的尺寸,也就是存放操作数这种类型的值所需要的存

储空间的字节数。尺寸是由操作数的类型确定。表达式 sizeof (char)的值始终为 1,但是

对于其他的类型计算出来的值可能会有所不同。在 16 位机上,表达式 sizeof (int)的值通

常为 2;在大多数 32 位机上,表达式 sizeof (int)的值为 4。执行下面的程序可以确定在

当前计算机系统中,各种基本数据类型的值所需要的存储空间是多少字节。 【例 3-9】 使用 sizeof 运算符。

/*程序 3-28*/

#include <stdio.h>

void main()

{

printf("char: %u\n",sizeof(char));

printf("int: %u\n",sizeof(int));

printf("short int: %u\n",sizeof(short int));

printf("long int: %u\n",sizeof(long int));

printf("float: %u\n",sizeof(float));

printf("dobule: %u\n",sizeof(double));

} 在 32 位计算机上,程序输出结果如下:

char: 1

int: 4

short int: 2

long int: 4

float: 4

double: 8

C 语言程序设计(第二版) 50

sizeof 运算符也可以应用于常量、变量和表达式。例如,表达式 sizeof (‘A’)的值是 1,表达式 sizeof (123)的值在 32 位机上是 4。如果有定义:

int a,b;

那么,表达式 sizeof (a)和 sizeof (a+b)的值在 32 位机上都是 4。

当应用于表达式时,sizeof 运算符不要求在表达式上加括号,可以用 sizeof a 代替

sizeof (a),但是,由于优先级的问题,括号有时还是会需要的。编译器会把 sizeof a+b解释为(sizeof a)+b,这是因为 sizeof 作为一元运算符的优先级(sizeof 运算符的优先级

是 2 级)高于二元运算符+。为了避免出现此类问题,所以建议始终在表达式上加括号。 sizeof 运算符并不对操作数进行求值,例如:

/*程序 3-29*/

#include <stdio.h>

void main()

{

int a=1;

printf("%u\n",sizeof(++a));

printf("a=%d\n",a);

} 在 32 位计算机上,程序输出结果如下:

4

a=1

3.3.7 表达式语句

在 C 语言中,任何表达式都可以通过增加分号变成一条语句。例如:

i++;

执行该语句将使变量 i 的值加 1。

j=++i;

执行该语句将使变量 i 的值加 1,然后取出变量 i 加 1 后的值,再赋值给变量 j。

a=1+2;

执行该语句将使变量 a 被赋值为 3。 但是有些表达式作为语句是没有意义的。例如:

a+b;

执行该语句将取出变量 a、b 的值,然后计算出 a+b 的值,该值被丢弃。变量 a、b 的

值没有发生任何改变。

学出版社

职教技术出版中心

www.abook.cn

第 3 章 数据的使用 51

【例 3-10】 输入三角形的三个边长 a、b、c,求三角形的面积。 已知边长计算三角形面积,可以利用海伦公式:

area s(s a)(s b)(s c)= - - -

其中1s (a b c)2

+ += 。

/*程序 3-30*/

#include <stdio.h>

#include <math.h>

void main()

{

float a,b,c,s,area;

printf("Input a,b,c:");

scanf("%f%f%f",&a,&b,&c);

s=1.0/2*(a+b+c);

area=sqrt(s*(s-a)*(s-b)*(s-c));

printf("area=%.2f",area);

} 为了完成平方根的计算,程序中使用了数学函数 sqrt,表达式 s*(s-a)*(s-b)*(s-c)作为

sqrt 函数的参数。为了使用数学函数,我们要在程序中包含头文件“math.h”。C 程序中

常用数学函数的介绍请参考附录。

3.4 数据类型转换

对于表达式 1+'a'*2.5 在 C 语言中是可以被计算的,并且有明确的值和类型。其中

的二元算术运算符+、*都要求两个操作数类型相同,所以在进行运算之前要把操作数

转换为同一类型。C 语言编译器可以自动进行所需的类型转换,所以这种转换被称为“隐

式转换”。同时,C 语言也提供了强制类型转换运算符来实现“显式转换”。

3.4.1 数据类型的隐式转换

隐式转换会在以下几种情况下发生: 1)需要算术操作数的运算符的操作数类型不一致时,比如算术运算符、关系运算符

要求操作数类型相同,此时进行一般算术转换。 2)赋值运算符的左操作数和右操作数类型不一致时。 3)函数调用中实际参数与对应的形式参数类型不一致时。 4)函数中 return 语句返回的值与函数返回值类型不一致时。 本章重点介绍前两种情况的类型转换,其他情况将在第 5 章进行介绍。

C 语言程序设计(第二版) 52

1.一般算术转换

C 语言允许在表达式中混合多种类型的数据进行运算,此时需要进行一般算术转换。

转换的基本策略是将尺寸较小的数据类型转换成尺寸相同或尺寸较大的数据类型。尺寸

大小是以该类型数据所占存储空间大小来衡量的。 一般算术转换需要先进行整型提升,表达式中的 char、short int、unsigned int 类型的

数据如果在 int 型的取值范围内,则转换成 int 型,否则转换成 unsigned int 型。整型提

升后的结果和原值相等。然后,按如下规则进行转换。 1)若一个操作数是 long double 类型,则另一个操作数将转换为 long double 类型。 2)若一个操作数是 double 类型,则另一个操作数将转换为 double 类型。 3)若一个操作数是 float 类型,则另一个操作数将转换为 float 类型。 4)若一个操作数是 unsigned long int 类型,则另一个操作数将转换为 unsigned long

int 类型。 5)若一个操作数是 long int 类型,则另一个操作数将转换为 long int 类型。 6)若一个操作数是 unsinged int 类型,则另一个操作数将转换为 unsigned int 类型。 【例 3-11】 一般算术转换实例:分析表达式的计算结果。 假设变量如下定义:

int a=1;

char ch='a';

float f=10.0;

double d=20.0; 表达式 a/ch+a*d-f/d 的计算过程如下。 1)计算 a/ch,首先取变量 ch 的值进行整型提升,结果 97(int 类型),然后计算

1/97,得结果 0(int 类型)。 2)计算 a*d,首先取变量 a 的值进行类型转换得结果 1.0(double 类型),然后计算

1.0*20.0,得结果 20.0(double 类型)。 3)计算 f/d,首先取变量 f 的值进行类型转换得结果 10.0(double 类型),然后计算

10.0/20.0,得结果 0.5(double 类型)。 4)计算 1)的结果加 2)的结果,首先对 1)的结果进行类型转换得结果 0(double

类型),然后计算 0+20.0,得结果 20.0(double 类型)。 5)计算 4)的结果减 3)的结果,即计算 20.0-0.5,得结果 19.5(double 类型)。 表达式 a/ch+a*d-f/d 的值为 19.5,类型为 double。

2.赋值运算的类型转换

进行赋值运算时类型转换的基本原则是:将赋值运算符右边表达式的值转换成赋值

运算符左边变量的类型。例如:

int a;

学出版社

职教技术出版中心

www.abook.cn

第 3 章 数据的使用 53

a=12.3; 常量 12.3 是 double 类型,变量 a 是 int 类型,因此要对 12.3 进行转换得到 int 类型

的结果,然后才能赋值给变量 a,处理方法是直接舍去 12.3 的小数部分,便得到 int 类型的结果为 12。

下面简单介绍各种情况下类型转换的方法。 (1)将尺寸小的类型转换成尺寸大的类型 这种情况通常是没有问题的,具体情况如下: 1)将尺寸小的浮点型值转换成尺寸大的浮点型值时,其值不变。 2)将尺寸小的整型值转换成尺寸大的整型值时,其值不变。 3)将整型值转换成浮点型值时,如果原值能被浮点型精确表示,其值不变,否则,

结果是原值的近似值。 【例 3-12】 类型转换。

/*程序 3-31*/

#include <stdio.h>

void main()

{

int a=12345;

double b;

b=a;

printf("b=%f",b);

} 程序输出结果如下:

b=12345.000000

(2)将尺寸大的类型转换成尺寸小的类型 这种情况可能得到无意义的结果。具体情况如下。 1)将尺寸大的浮点型值转换成尺寸小的浮点型值时,若要转换的值超出了可表示的

范围,则出现溢出错误;若要转换的值在可表示的范围内且能确切表示时,转换结果

和原值相等;若要转换的值在可表示的范围内但不能确切表示时,转换结果是原值的

近似值。 2)将尺寸大的整型值转换成尺寸小的整型值时,若要转换的值在可表示的范围,其

值不变;若要转换的值超出了可表示的范围,将得到一个无意义的值。 3)将浮点型值转换成整型值时,小数部分将被直接舍去,若整数部分能被该整型表

示,转换结果是整数部分;否则,将得到一个无意义的值。 【例 3-13】 类型转换。

/*程序 3-32*/

#include <stdio.h>

void main()

{

C 语言程序设计(第二版) 54

double a=123.45;

int b;

b=a;

printf("b=%d",b);

} 程序输出结果如下:

b=123

(3)字符型与整型的转换 字符型值向整型值转换时,unsigned char 类型值将被作为无符号整数进行处理。char

类型值既可以作为有符号整数也可以作为无符号整数进行处理,多数计算机系统是作为

有符号整数进行处理的。 整型值向字符型值转换时,若要转换的值在字符型的可表示范围内,其值不变;否

则,将得到一个无意义的值。 【例 3-14】 将一个大写字母转换成对应的小写字母。

/*程序 3-33*/

#include <stdio.h>

void main()

{

char ch1,ch2;

printf("Input a capital letter:");

ch1=getchar();

ch2=ch1+32;

printf("%c,ASCII is %d\n",ch1,ch1);

printf("%c,ASCII is %d",ch2,ch2);

} 程序运行后输入:

A↙ 然后程序输出:

A,ASCII is 65

a,ASCII is 97 程序分别输出了字母 A 和 a 以及它们的 ASCII 码值(采用十进制形式)。字符型数

据在 C 语言中是按照整数的方式进行处理的。因为字符数据在计算机中是以二进制编码

的形式存放的( 常用的是 ASCII 编码),我们可以直接将这些编码看成一个整数,所

以字符数据和整型数据一样可以进行算术运算。另外,采用%d 格式可以输出字符的

编码值。 (4)有符号整型与无符号整型的转换 有符号整数转换为相同或更大尺寸的无符号整数时,若有符号整数为非负数,则其

学出版社

职教技术出版中心

www.abook.cn

第 3 章 数据的使用 55

值不变;否则,通过将其值加上比该无符号整型所能表示的 大值大 1 的数而转换成无

符号数。 有符号整数转换为尺寸小的无符号整数时,其结果是该值除以比该无符号整型所能

表示的 大值大 1 的数后得到的非负余数。 【例 3-15】 有符号数和无符号数的转换。

/*程序 3-34*/

#include <stdio.h>

void main()

{

unsigned int u;

unsigned short int su;

int i;

i=-100000;

u=i;

su=i;

printf("u=%u,su=%u",u,su);

} 程序输出结果(在 VC 6.0 环境下)如下:

u=4294867296,su=31072 在 VC 6.0 环境下,unsigned int 型数据占 4 个字节, 大值为 4294967295;unsigned

short int 型数据占 2 个字节, 大值为 65535.;int 型数据占 4 个字节。

3.4.2 强制类型转换运算符

为了使程序设计人员更灵活地控制类型转换,C 语言提供了强制类型转换运算符。 强制类型转换运算符的表示形式如下:

(类型名) 强制类型转换表达式的格式如下:

(类型名)表达式 强制类型转换运算符的作用是将右侧表达式的值转换成指定类型的值。比如(int)

12.3 的结果是整数 12,此结果通过将 12.3 转换成整型值而得到。 强制类型转换运算符是一元运算符,其优先级为 2 级。例如表达式(float)1/2 等

价于((float)1)/2,先将整数 1 转换成浮点数,然后将进行浮点数的除法运算,结

果是 0.5。 强制类型转换运算不会改变右侧表达式的值和类型,而是得到一个指定类型的结果

值。例如:

float f=1.2;

int i;

C 语言程序设计(第二版) 56

i=(int)f; 赋值后变量 i 的值为 1,而变量 f 的值仍然为 1.2。

习 题

一、选择题 1.有以下程序:

#include <stdio.h>

void main()

{

char a,b,c,d;

scanf("%c%c",&a,&b);

c=getchar();

d=getchar();

printf("%c%c%c%c\n",a,b,c,d);

} 当执行程序时,按下列方式输入数据:

12↙

34↙ 则输出结果是( )。

A.1234 B.12 C.12 D.12 3 34

2.有以下程序:

#include <stdio.h>

void main()

{

int k=011;

printf("%d\n",k++);

} 程序输出结果是( )。

A.12 B.11 C.10 D.9 3.以下叙述中正确的是( )。

A.调用 printf 函数时,必须有输出项 B.使用 putchar 函数时,必须在之前包含头文件 stdio.h C.在 C 语言中,整数可以以二进制、八进制或十六进制的形式输出 D.调用 getchar 函数读入字符时,可以从键盘上输入字符所对应的 ASCII 码

学出版社

职教技术出版中心

www.abook.cn

第 3 章 数据的使用 57

4.设变量已正确定义,若要通过 scanf("%d%c%d%c",&a1,&c1,&a2,&c2);语句为变

量 a1 和 a2 赋数值 10 和 20,为变量 c1 和 c2 赋字符 X 和 Y,以下输入形式中正确的

是( )。 A.10 X 20 Y↙ B.10 X20 Y↙ C.10 X↙ 20 Y↙ D.10X↙ 20Y↙

5.若变量已正确定义为 int 型,要通过语句 scanf("%d,%d,%d",&a,&b,&c);给 a 赋

值 1,给 b 赋值 2,给 c 赋值 3,以下输入形式中错误的是( )。 A. 1,2,3↙ B.1 2 3↙ C.1, 2, 3↙ D.1,2,3↙

6.表达式 3.6-5/2+1.2+5%2 的值是( )。 A.4.3 B.4.8 C.3.3 D.3.8

7.若变量 x、y 已正确定义并赋值,以下符合 C 语言语法的表达式是( )。 A.++x,y=x-- B.x+1=y C.x=x+10=x+y D.double(x)/10

8.已知大写字母 A 的 ASCII 码是 65,小写字母 a 的 ASCII 码是 97,以下不能将变

量 c 中的大写字母转换为小写字母的语句是( )。 A.c=(c-'A')%26+'a'; B.c=c+32; C.c=c-'A'+'a'; D.c=('A'+c)%26-'a';

9.有以下程序:

#include <stdio.h>

void main()

{

int x,y,z;

x=y=1;

z=x++,y++,++y;

printf("%d,%d,%d\n",x,y,z);

} 程序输出结果是( )。

A.2,3,3 B.2,3,2 C.2,3,1 D.2,2,1 10.有以下程序:

#include <stdio.h>

void main( )

{

int a=1,b=0;

printf("%d,",b=a+b);

printf("%d",a=2*b);

} 程序输出结果是( )。

A.0,0 B.1,0 C.3,2 D.1,2

C 语言程序设计(第二版) 58

二、填空题 1.以下程序的输出结果是______________。

#include <stdio.h>

void main()

{

int x=0210;

printf("%X\n",x);

}

2.执行以下程序时输入 1234567↙,则输出结果是______________。

#include <stdio.h>

void main()

{

int a=1,b;

scanf("%2d%2d",&a,&b);

printf("%d %d\n",a,b);

} 则输出结果是______________。 3.若整型变量 a 和 b 中的值分别为 7 和 9,要求按以下格式输出 a 和 b 的值:

a=7

b=9 请完成输出语句:

printf("_____________",a,b);

4.以下程序的输出结果是______________。

#include <stdio.h>

void main()

{

char c; int n=100;

float f=10; double x;

x=f*=n/=(c=50);

printf("%d %f\n",n,x);

}

5.已知字母 A 的 ASCII 码为 65。以下程序的输出结果是______________。

#include <stdio.h>

void main()

{

char a, b;

a='A'+'5'-'3'; b=a+'6'-'2' ;

printf("%d %c\n", a, b);

}

学出版社

职教技术出版中心

www.abook.cn

第 3 章 数据的使用 59

6.以下程序的输出结果是______________。

#include <stdio.h>

void main()

{

int m=011,n=11;

printf("%d %d\n",++m,n++);

}

7.以下程序的输出结果是______________。

#include <stdio.h>

void main()

{

int a=10;

a=(3*5, a+4);

printf("a=%d\n",a);

}

8.设变量已正确定义为整型,则表达式 n=i=2,++i,i++的值为______________。 9.表达式(int)((double)(5/2)+2.5)的值是______________。 10.已知 a=6,则赋值表达式 a+=a-=a*=a 的值是______________。 三、编程题 1.编写程序,输入长方体的长、宽、高,求长方体的体积。 2.编写程序,输入圆半径,求圆的周长和面积。 3.某超市营业员工资的计算方法是:每月 800 元的基本工资加该月销售额的 8.5%

提成。编写程序,输入营业员的月销售额,计算并输出该营业员的月收入。 4.编写程序,输入一个实数,使该实数的值保留两位小数,并对第三位进行四舍五

入(规定实数为正数)。 5.编写程序,将两个两位数的正整数 a、b 合并形成一个整数放在 c 中。合并方式:

将 a 的十位和个位数依次放在 c 的个位和十位上,b 的十位和个位数依次放在 c 的百位

和千位上。