从零开始学C语言(第2版)(不提供光盘内容)(txt+pdf+epub+mobi电子书下载)


发布时间:2020-07-18 06:24:00

点击下载

作者:戴晟晖等

出版社:电子工业出版社

格式: AZW3, DOCX, EPUB, MOBI, PDF, TXT

从零开始学C语言(第2版)(不提供光盘内容)

从零开始学C语言(第2版)(不提供光盘内容)试读:

前言

C语言的诞生是现代程序语言革命的起点,是程序设计语言发展史中的一个里程碑。——Dennis M Ritchie(C语言之父)

近年来,C语言是应用最为广泛的一种高级程序设计语言,它不仅是计算机专业学生的必修课,也是许多非计算机专业学生所青睐的技术学科。C语言程序设计是全国和各省计算机等级考试的重要考试内容。C语言功能丰富,表达能力强,使用灵活方便,程序效率高,是结构化程序设计语言。C语言具有很强的实用性,既可用来编写应用软件,也适合于编写系统软件。本书的特点

C语言是学习其他语言的基础,读者只要掌握C语言,学习其他语言就会很快入门。本书为了使读者能够从C语言的初学者成为编程高手,专门对C语言知识进行研究分析。本书的主要特点如下: 结构清晰明了。本书共18章,每章都分为若干节,每节一个小

知识点,结构层次清晰可见。 内容全面详细。本书涵盖了C语言中的所有知识,并将C语言各

个知识点作为单独章节进行讲解,并举出大量实例。 讲解由浅入深。向读者介绍C语言的基本理论知识、数据结构和

基本的编程规则,让读者对C语言的基本知识及结构化程序设计

思想有一个初步的认识;接着对C语言一些复杂的数据结构类型

如数组、函数、指针操作、结构体与共用体、文件等进行详细的

讲解。 实例丰富多样。本书所讲的每一个知识点都运用充分的实例进行

讲解说明,便于读者掌握。

1.清晰的体例结构

① 知识点介绍 准确、清晰是其显著特点,一般放在每一节开始位置,让零基础的读者了解相关概念,顺利入门。

② 实例 书中出现的完整实例,以章节顺序编号,便于检索和循序渐进地学习、实践,各实例均放在每节知识点介绍之后。

③ 实例代码 与实例编号对应,层次清楚、语句简洁、注释丰富,体现了代码优美的原则,有利于读者养成良好的代码编写习惯。

④ 运行结果 对实例给出运行结果和对应图示,帮助读者更直观地理解实例代码。

⑤ 习题 每章最后提供专门的测试习题,供读者检验所学知识是否牢固掌握。

⑥ 贴心的提示 为了便于读者阅读,全书还穿插着一些技巧、提示等小贴士。体例约定如下:

提示:通常是一些贴心的提醒,让读者加深印象或提供建议,或者解决问题的方法。

注意:提出学习过程中需要特别注意的一些知识点和内容,或者相关信息。

警告:对操作不当或理解偏差将会造成的灾难性后果做警示,以加深读者印象。

● 电子教案(PPT)

本书可以作为高校相关课程的教材或课外辅导书,所以笔者特别为本书制作了电子教案(PPT),以方便老师教学使用。

3.供完善的技术支持

本书提供了交流论坛:http://www.rzchina.net,读者可以在上面提问、交流。另外,论坛上还有一些小的教程、视频动画和各种技术文章,可帮助读者提高开发水平。适合阅读本书的读者 C语言的初、中级读者。 了解C语言,但所学不全面的人员。 高等理科院校学习C语言课程的学生。 使用C语言进行毕业设计的学生。 熟悉其他语言,以此作为参考书的开发人员。本书作者

本书主要由戴晟晖和冯志强编写。其他参与本书编写的人员有曾光、张双、朱照华、黄永湛、张贺军、李勇、关涛、王岩、李晓白、魏星、刘蕾、品峰军、张增强。在此一并表示感谢!

第1篇 C语言入门

第1章 C语言入门基础

C语言作为国际上流行的计算机高级语言,能实现多种功能。为使读者能够对C语言有一个全面的认识,本章在介绍C语言之前,简单介绍了很多其他的相关知识。 计算机语言的演变; 数制、数制转换与存储; 程序设计思想——算法; C语言的发展简史和特点。

1.1 计算机语言的演变

计算机语言的发展是一个不断演变的过程,从开始的机器语言到汇编语言到各种结构化高级语言,最后到支持面向对象技术的面向对象语言。1.1.1 机器语言

机器语言是第一代计算机语言。计算机所使用的是由“0”和“1”组成的二进制数。二进制是计算机语言的基础,所以也称为二进制语言。机器语言指用机器码书写程序,不易被人们识别和读写,所以使用机器语言是十分痛苦的,特别是在程序有错需要修改时,更是如此。而且,由于每台计算机的指令系统往往各不相同,所以在一台计算机上执行的程序,要想在另一台计算机上执行,必须另编程序,造成了重复工作。但由于计算机能够直接识别程序中的指令,故而运算效率是所有语言中最高的,这种用二进制编写的程序也叫“目标程序”。1.1.2 汇编语言

汇编语言又称符号语言,对机器指令进行简单的符号化,它也是利用计算机所有硬件特性并能直接控制硬件的语言。人们为了减轻使用机器语言编程的痛苦,对机器语言进行了一种有益的改进:用一些简洁的英文字母、符号串来替代一个特定的指令的二进制串,比如,用“ADD”表示加法,“MOV”表示数据传递等,因此,人们就能理解程序所进行的操作,方便用户对程序进行纠错及维护。汇编语言属于第二代计算机语言。汇编语言十分依赖于机器硬件,它像机器指令一样,是硬件操作的控制信息,因而仍然是面向机器的语言。针对计算机特定硬件编制的汇编语言程序,能准确发挥计算机硬件的功能和特长,程序精炼而质量高,但是汇编语言的通用性不强,可移植性不好,使用起来比较烦琐,很费时。汇编语言的效率仍十分高,所以至今仍是一种常用而强有力的软件开发工具。1.1.3 高级语言

1958年首次出现了一种描述加工过程很方便、并且能在任何计算机上使用的第三代程序设计语言。程序设计人员可以利用这种语言直接写出各种表达式来描述简单的计算机过程,这种语言称为高级语言。这种语言接近于数学语言或人的自然语言,同时又不依赖于计算机硬件,编出的程序能在所有机器上通用。使用较普遍的有FORTRAN、ALGOL、COBOL、BASIC、LISP、SNOBOL、PL/1、Pascal、C、PROLOG。

用高级语言编写的程序称为“源程序”。源程序不能在计算机上直接运行,必须将其翻译成二进制程序后才能执行。翻译有两种方式:解释程序和编译程序。解释程序是一次只读一行源程序,并执行该行语言指定的操作,每次运行用户程序时,必须要用解释程序。在程序的开发过程中,运用解释的方式执行程序,便于程序员对程序进行调试。编译程序是将源程序全部翻译成目标代码即二进制程序后再执行,只读取一次,节省了大量的时间。1.1.4 面向对象或面向问题的高级语言

第四代语言是使用第二代、第三代语言编制而成的。面向对象的语言是在面向过程的计算机语言的基础上发展而来的,如C++语言就是由C语言发展而来的。所谓面向对象,就是基于对象的概念,以对象为中心,类和继承为构造机制,认识、了解、刻画客观世界并开发出相应的软件系统。它把构成问题的事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描述某个事物在整个解决问题的步骤中的行为。比较典型的代表面向对象程序设计语言有C++、Visual Basic、Delphi等。

1.2 数制、数制转换与存储

上一节我们了解到高级语言在执行的过程中要解释或编译成二进制代码,即转换成计算机语言才能被识别。C语言程序在执行的过程中要将源程序解释或编译成目标程序,因此在开始学习C语言之前我们先学习一下数制及不同数制之间的转换和存储。1.2.1 数制

数制也称计数制,是指用一组固定的符号和统一的规则来表示数值的方法。计算机处理的信息必须转换成二进制形式数据后才能进行存储和传输。计算机中,经常使用的进制有二进制、八进制、十进制、十六进制。1.二进制数

二进制数由两个基本数字0、1组成,二进制数的运算规律是逢二进一。二进制数的书写通常在数的右下方注上基数2,或在后面加B与其他进制加以区别,如二进制100101可以写成(100101)或写2成100101B。

二进制数的加法和乘法运算如下:2.八进制数

八进制是由0~7共8个数字组成的,运算规则是逢八进一。八进3制的基R=8=2,并且每个数码正好对应三位二进制数,所以八进制能很好地反映二进制。八进制数据表示时用下标8或数据后面加O表示,如八进制261写成(261)、(261)O。83.十进制数

十进制数是我们常用的数据表示方法,由0~9共10个数字组成,运算规则是逢十进一。表示时用下标10或数据后面加D,也可以省略。4.十六进制数

十六进制数由0~9及A~F共16个数字组成,A~F分别表示十进制数10~15,运算规则是逢十六进一。通常在表示时用下标16或数据后面加H,如(1FA)或(1FA)H。16Tips 在C语言程序中,我们可以通常按十进制写,如果写的是十六进制,需要以0x开头;八进制以0开头,如0123表示八进制的123,0x123,则表示十六进制的123。1.2.2 数制的转换

我们知道计算机中数据是以二进制的形式存在的,但是二进制数据太长,没有人愿意对很长的二进制进行操作,用十六进制或八进制可以解决这个问题。因为进制越大,数的表达长度也就越短。不过,为什么偏偏是十六或八进制,而不是其他的诸如九或二十进制呢?因为2、8、16,分别是2的1次方、3次方、4次方,这一点使得三种进制之间可以非常直接地互相转换。八进制或十六进制既缩短了二进制数又保持了二进制数的表达特点。1.二进制、八进制、十六进制转换成十进制

规则:数码乘以各自的权的累加。【实例1-1】其他进制转换成十进制。40(10001)B=2+2=1720-2(101.01)B=2+2+2=5.2510(011)O=8+8=910(72)O=7*8+2*8=583210(112A)H=1*16+1*16+2*16+10*16=43942.十进制转换成二进制、八进制、十六进制

规则如下。 整数部分:除以进制取余数,直到商为0,余数从下到上排列。 小数部分:乘以进制取整数,得到的整数从上到下排列。【实例1-2】十进制转换成其他进制。(1)十进制20.345转换成二进制

20.345D=10100.01011B(2)十进制100转换成八进制、十六进制3.二进制转换成八进制

规则如下。 整数部分:从右向左按三位进行分组,不足补零。 小数部分:从左向右按三位进行分组,不足补零。【实例1-3】将二进制数(1101101110.110101)转换成八进制2数。4.二进制转换成十六进制

规则如下。 整数部分:从右向左按四位进行分组,不足补零。 小数部分:从左向右按四位进行分组,不足补零。【实例1-4】将二进制数(001101101110.110101)转换成十六进2制数。5.八进制、十六进制转换成二进制

规则如下。 一位八进制对应三位二进制。 一位十六进制对应四位二进制。【实例1-5】八进制、十六进制转换成二进制。1.2.3 计算机中数据的存储

我们已经知道在计算机内所有数据最终都是使用二进制数表示的,上一节中我们已经学习了如何将一个十进制、八进制、十六进制数转换为二进制数。数值有正负之分,它们在计算机内是如何存储的呢?

在计算机中,数据有三种表示方法:原码、反码和补码。计算机用一个二进制的最高位存放所表示数值的符号,最高位为0表示正数,最高位为1表示负数。对于一个正数,原码是将该数转换成二进制,它的反码和补码与原码相同。对于一个负数,原码是将该数按照绝对值大小转换成的二进制数,最高位即符号位为1;它的反码是除符号位外将二进制数按位取反,所得的新二进制数称为原二进制数的反码;它的补码是将其二进制的反码加1。计算机中任何一个带有符号的二进制数都是以补码形式进行运算和存储的。

如表1-1所示为比较1与-1的原码、反码和补码。表1-1 数据存储表(1与-1的原码、反码和补码)注:为了方便比较我们在这里用一个字节的整数举例。

1.3 程序设计思想——算法

在我们遇到问题的时候,首先在大脑中形成一种解题思路,然后根据可行的思路运用具体的步骤解决问题。在程序设计中,也需要有一种编程思路,这就是算法。1.3.1 算法的概念

广义上的算法指的是解决问题的方法。就程序设计而言,算法是指计算机求解某一问题而采用的具体方法、步骤。事实上,在日常生活中解决问题经常要用算法,只是通常不用“算法”这个词罢了,例如,乐谱是乐队指挥和演奏的算法;菜谱是厨师做菜的算法,等等。

在程序设计中,算法应该能够离散成具体的若干个操作步骤,每一个步骤都是能够用程序设计语言提供的语句或者语句串来完成的。

例如,求两个整数中较大的数。解决这个问题的算法如下:

第1步 开始。

第2步 输入两个整数a、b。

第3步 比较a、b的大小,如果a>b,输出a,否则输出b。

第4步 结束。

需要注意的是,程序是有开始和结束的,所以算法必须有“开始”和“结束”这两个步骤。

计算机解题算法分为两大类:数值运算算法和非数值运算算法。数值运算算法解决的是求数值的问题,运用一定的求值公式如二元一次方程的求根公式、圆面积的计算公式等。这类算法相对比较成熟。非数值运算的算法涉及的内容比较广,而且难以量化,一般都需要参考已有的类似算法,针对具体问题重新设计。1.3.2 算法的特点

解决问题我们需要一个可行的算法,而如何去衡量这个算法是否得当,是否可行呢?通常,算法具有以下5个重要的特征。1.有穷性

一个算法应包括有限个操作步骤,其中每一步都应在合理的时间范围内完成。有的可能要花很长的时间来执行指定的任务,但仍将在一定的时间内终止。它执行的时间没有严格的限制,受所要处理问题的约束。2.确定性

算法在指导计算执行每步程序时,这些指令都是明确的,没有任何歧义。例如:输出:A/正整数

是无法执行的,因为正整数指的是一类数,没有指定A除以哪一个正整数,所以这个步骤是不确定的。3.有效性

算法中的每个步骤都应该是有意义、能够有效执行的,并能得到确定的结果。比如,开方运算的数不能是负数;分母不能够为0。4.输入

一个算法有零个或多个输入。在某些算法中,所需要的数据可以由用户用输入设备输入,例如,求两个数中的较大值,这两个数可以是用户随意输入的两个数,它们的值是不确定的。另外在编程的过程中也可以直接用两个确定的数进行比较,这时就不需要用户的输入,即零输入。5.输出

一个算法有一个或多个输出。算法的输出反映了输入数据加工后的结果,没有输出的算法是毫无意义的。例如:求两个数的最大公约数,执行后,若这两个数有最大公约数就输出,若没有最大公约数就输出“这两个数无最大公约数”给用户以反馈。1.3.3 算法的表示方法

一个算法可以用多种不同的方法来描述,有自然语言、伪代码、流程图、N-S图等,每种表示方法都有自己的优缺点,可以根据不同的需要选择适当的表示方法。1.自然语言

使用自然语言描述算法是指用文字加上一些必要的数学符号来描述解决问题的算法。【实例1-6】用自然语言描述100以内正整数的和。(1)设S代表总和,N代表正整数;(2)S=0,N=1;(3)S=S+N,原来的和加上一个正整数;(4)N=N+1,把下一个正整数赋给N;(5)判断N是否小于100,如果是则跳转到步骤(3),否则跳转到步骤(6);(6)输出S的值。

自然语言表示法,除了很简单的问题外,一般不用这种方法。从上面的例子中我们不难发现,自然语言容易理解,也比较容易掌握,但需要用大量的文字进行解释说明,不直观,当算法中含有多分支或循环操作时很难表述清楚。2.流程图

流程图很好地弥补了自然语言描述算法的缺陷,它用标准的图形元素来描述算法步骤,程序结构一目了然。流程图中最基本、最常用的构件如表1-2所示。表1-2 常用流程图构件

用流程图表示100以内的正整数的算法如图1-1所示。图1-1 100以内正整数的流程图表示

算法从“开始”执行,依次按照流程顺序执行了前四个输入框内的动作,到达判断框后先判断N是否满足条件,如果条件成立,即判断的结果为True(简写为T),沿着T所示的流程返回到第三步开始继续执行,如果判断条件不成立,即判断的结果为False(简写为F),沿着F所示的流程线向下执行,最后输出S。

使用流程图来描述算法比较自由灵活,形象直观,可以用来表示任何算法,但是绘制流程图之前要弄清哪些是判断的条件,哪些是处理的操作,而且绘图也比较麻烦,并且需要用箭头表示程序流程的方向,随意性太大。3.伪代码

伪代码是一种在算法开发过程中用来表达思想的非形式化的符号系统。相对于编程语言来说,它的语法规则、语义结构等规定限制比较宽松,是一种更加易用的表示系统。

将【实例1-6】用伪代码描述表示如下:①N←1;②S←0;③do while N≤100④{S←S+N;N←N+1;}⑤print S

伪代码通常采用自然语言、数学公式和符号描述算法的操作步骤,同时采用计算机高级语言的控制结构来描述算法步骤的执行顺序。但前提是必须熟悉某种程序设计语言,软件专业人员一般习惯使用伪代码。4.N-S图

N-S图也被称为盒图。在使用流程图的过程中,人们发现流程线不一定是必需的,为此,又设计出了一种新的流程图,把整个程序写在一个大框图内,这个大框图由若干个小基本框图构成,这种流程图简称为N-S图。

在结构化程序设计中,用N-S图表示顺序结构、选择结构和循环结构的结构有所不同,具体表示如下。(1)顺序结构N-S图

如图1-2中的顺序结构图所示,程序执行的顺序按照矩形块出现的顺序从上向下依次执行,先执行A块再执行B块。图1-2 三种结构的N-S图表示(2)选择结构N-S图

如图1-2中的选择结构图所示,程序执行到此步时,先判断条件的真假,如果为真就执行A块,如果为假就执行B块。(3)循环结构N-S图

如图1-2中的循环结构图所示,循环结构的N-S图有两种结构,一种是当型循环,一种是直到型循环。当型循环指的是先判断条件,如果条件为真就执行循环体,否则就跳过循环体执行下面的程序。直到型循环指的是先执行一次循环体,然后再判断条件,如果条件为真就返回继续执行循环体,否则就不执行循环体,开始向下执行。

将【实例1-6】用N-S图描述表示如图1-3所示。图1-3 100以内正整数的N-S图表示

N-S图弥补了流程图自由性大、任意转移控制的缺点,表示嵌套关系方便直观,对模块和控制结构的层次和作用域显示行比较清晰;但是N-S图修改起来没有流程图方便,如果分支嵌套层次一多就比较难画了。1.3.4 算法分析

算法分析是对一个算法需要多少计算时间和存储空间做定量的分析。求解一个给定的可计算或可解的问题,不同的人可以编写出不同的程序,分析算法可以预测这一算法适合在什么样的环境中有效地运行,对解决同一问题的不同算法的有效性做出比较。

什么样的算法才是一个好的算法呢?通常从下列几个方面衡量算法的优劣。1.正确性

也称为有效性,是指算法能满足具体问题的要求,即对任何合法的输入,算法都会得出正确的结果。确认正确性的根本方法是进行形式化的证明。但对一些较复杂的问题,这是一件相当困难的事。许多计算机科学工作者正致力于这方面的研究,目前尚处于初级阶段。因此,实际中常常用测试的方法验证算法的正确性。2.可读性

指算法被理解的难易程度。人们常把算法的可读性放在比较重要的位置,主要是因为晦涩难懂的算法不易交流和推广使用,也难以修改、扩展与调试,而且可能隐藏较多的错误。3.健壮性

即对非法输入的抵抗能力。它强调的是,如果输入非法数据,算法应能加以识别并做出处理,而不是产生错误动作或陷入瘫痪。4.时间复杂度与空间复杂度

算法的时间复杂度指算法需要消耗的时间资源,也就是说算法的运行时间。一般来说,计算机算法是问题规模n的函数f(n),算法的时间也因此记为:T(n)=O(f(n))

因此,问题的规模n越大,算法执行的时间的增长率与f(n)的增长率正相关,称为渐进时间复杂度。

算法的空间复杂度是指算法需要消耗的空间资源。其计算和表示方法与时间复杂度类似,一般都用复杂度的渐近性来表示,记为:S(n)=O(f(n))

1.4 C语言的发展简史和特点

C语言能够快速发展成为最受欢迎的语言之一,主要是因为它具有强大的功能。它既有高级语言的特点,又具有汇编语言的特点。它可以作为工作系统设计语言,编写系统应用程序,也可以作为应用程序设计语言,编写不依赖于计算机硬件的应用程序。在开始正式学习C语言之前我们先对C语言的发展历程及它的特点做简单的了解。1.4.1 C语言的诞生与发展

C语言诞生于1972年,是由著名的美国贝尔实验室科学家D.M.Ricthie发明的。C语言的原型是ALGOL 60语言。为了更好地开发新版本的UNIX,D.M.Ricthie在B语言的基础上设计了C语言。除了系统的最核心部分,UNIX的后来版本基本都是用C开发的。C语言后来又被多次改进,并出现了多种版本。1.C语言诞生的背景

我们知道汇编语言程序依赖于计算机硬件,其可读性和可移植性都很差;但一般的高级语言又难以实现对计算机硬件的直接操作(这正是汇编语言的优势)。于是人们盼望有一种兼有汇编语言和高级语言特性的新语言。

1963年,剑桥大学将ALGOL 60语言发展成为CPL(Combined Programming Language)语言。C语言的原型就是ALGOL 60语言。

1967年,剑桥大学的Martin Richards对CPL语言进行了简化,于是产生了BCPL语言。

1970年,美国贝尔实验室的Ken Thompson将BCPL进行了修改,并为它起了一个有趣的名字“B语言”,意思是将CPL语言煮干,提炼出它的精华,并且他用B语言写了第一个UNIX操作系统。而在1972年,B语言也给人“煮”了一下,美国贝尔实验室的D.M.Ritchie在B语言的基础上最终设计出了一种新的语言,他取了BCPL的第二个字母作为这种语言的名字,这就是C语言。2.C语言的发展历程

1972年,贝尔实验室D.M.Ritchie设计出C语言,当时Ken Thompson刚刚使用汇编语言和B语言开发出UNIX操作系统,但用汇编语言开发系统非常烦琐,于是D.M.Ritchie用C语言改写UNIX系统的内核。

为了推广UNIX操作系统,1977年D.M.Ritchie发表了不依赖于具体机器系统的C语言编译文本《可移植的C语言编译程序》。

C语言在1978年由美国电话电报公司(AT&T)贝尔实验室正式发布。由B.W.Kernighan和D.M.Ritchie共同完成了著名的《The C Programming Language》一书,通常简称为《K&R》。

1983年,美国国家标准化协会(ANSI),根据C语言问世以来各种版本对C语言的发展和扩充,制定了ANSI C 标准。

由于《K&R》中并没有定义一个完整的标准C语言,K&R第一版在很多语言细节上也不够精确,所以ANSI于1983年夏天,在CBEMA的领导下建立了X3J11委员会,目的是产生一个C标准。X3J11在1989年末提出了一个他们的报告[ANSI 89]。

1990年,国际标准化组织ISO(International Organization for Standards)接受了89 ANSI C为I SO C的标准(ISO9899-1990)。

目前C语言在世界范围内都是相当流行的高级语言。C语言最初是为了描述和实现UNIX系统的,但随着C语言的发展,它适用于任何平台,C语言可以用来编写应用软件,也可以用来编写系统软件。许多著名的系统软件,如DBASE IV都是由C语言编写的。用C语言加上一些汇编语言子程序,就更能显示C语言的优势了,像PC-DOS、WORDSTAR等就是用这种方法编写的。1.4.2 C语言的特点

一种语言之所以能够存在和发展,并具有生命力,在于它具有一些不同于或者说优于其他语言的特点。C语言具有以下几个基本特点。1.紧凑简洁、灵活方便

与学习自然语言一样,掌握任何程序设计语言都需要掌握一些关键字(也称为保留字),即基本词汇。C语言一共只有32个保留字,9种控制语句,压缩了一切不必要的成分,相对于其他语言,C语言的关键字比较少,便于记忆。另外,完成同样的任务,C程序往往比其他语言的程序短,因此输入程序时工作量少,有利于提高程序员的编程效率。2.运算符丰富多样

C语言具有种类丰富的运算符,共34种运算符和15个等级的运算优先顺序,除了具有一般高级语言使用的算术运算符、关系运算符及逻辑运算符外,还有自增、自减运算符,复合赋值运算符,3项条件运算符和位运算符等。另外,C语言还把括号、赋值、强制类型转换等都作为运算符处理。如此丰富的运算符使运算表达式简洁多样化,且编译处理也统一简单,灵活地使用这些运算符可以实现在其他语言中难以实现的运算。3.数据结构多样性

C语言的数据类型有整型、实数型、字符型、数组类型、指针类型、结构体类型、联合体类型及枚举类型等,可以实现各种复杂的数据结构的运算,特别是指针类型,使用起来更是灵活、多样。因此,C语言具有较强的数据处理能力。4.程序语言模块化

C语言程序由许多个函数构成,各个函数之间相互独立,这样不仅有利于把整体程序分割成若干个具有相对独立功能的模块,而且便于模块间相互调用及相互传递数据。5.控制语句结构化

C语言为结构化程序设计提供了if-else、switch-case、while、do-while、for等流程控制语句,便于采用自顶向下、逐步细化的结构化程序设计方法,符合现代编程风格的要求。6.接近硬件与系统

C语言既有高级语言的特点,又具有汇编语言的特点,能够用来开发系统程序。C语言允许程序根据地址直接访问内存,允许程序按位处理数据,也可以直接对硬件进行操作。7.运行效率高

C语言编写的程序生成目标代码质量高,程序执行效率高,一般只比汇编程序生成的目标代码效率低10%~20%。在相同的计算机上完成相同的任务,C程序往往比其他语言的程序运行时间短,占用的内存空间少。8.可移植性好

在一种计算机上开发的C程序,经过少量的修改,甚至不经修改,就可以在其他类型的计算机上运行。它适合于多种操作系统,如DOS、NUIX,也适用于多种机型。

任何一种语言都有各自的优点也有自己的缺点。C语言有着众多的优点也有一些弱点,比如运算符的优先级比较多,有些还与常规约定不同,不便记忆;C语言的语法限制不太严格,对变量的类型约束不严格,影响程序的安全性;对数组的下标越界不做检查等。对初学者来说,必须掌握C语言的基础知识,只有熟练掌握了才能灵活运用。

总体上说,C语言功能强大,灵活易用,对编程人员的限制少,可以编写出任何类型的程序(系统软件与应用软件)。同时,C语言作为一门基础性语言掌握之后,学习其他语言会很快入门。

1.5 本章小结

计算机语言经历了运用二进制代码编写的第一代机器语言、相对来说容易理解的第二代汇编语言,到后来的第三代高级语言运用完全接近人类习惯的英语单词表示,以及现在面向对象或问题的高级语言。程序语言的发展越来越向人类可以接受的方向发展,而计算机只能识别二进制代码,因此数据在计算机内部需要进行转换才能识别存储,常用的数制有二进制、八进制、十进制和十六进制。二进制、八134进制和十六进制有着特殊的对应关系,它们分别是2,2,2,进制之间易于转换。

程序设计就是要找出解决问题的算法,并把它用程序语言描述出来。因此首先要学习程序设计的思路即解决问题的思想。

1.6 习题

1.算法的特点有哪些?

2.为什么说C语言是一门“中间语言”?

3.简述C语言的发展历史。

第2章 认识C语言程序

计算机语言与人类的自然语言一样,都有自己特定的结构特征与书写风格。只有遵循特定的语法结构,程序才能被识别;通过正确的书写风格,增强程序的可读性,程序员之间才能更好地交流。本章通过一个简单的C语言程序例子来说明C语言程序的结构和书写格式,帮助初学者对C语言程序建立初步的认识,以便于进一步学习。 C语言程序的结构特征; C语言程序的书写风格; C语言程序的开发过程; 熟悉Visual C++ 6.0集成开发环境; 用Visual C++ 6.0运行一个C语言程序。

2.1 C语言程序的结构特征

一种语言只有熟悉它的语法和结构特征,才能更好地理解并掌握它。下面我们通过一个简单的C语言程序实例来对其结构进行分析,使读者对C语言程序有一个初步的认识,在以后的章节中,我们将进行深入的讲解。

下面是用C语言编写的一个程序,实现的功能是从输入端任意输入两个整型数,并求这两个数的和,最后输出到屏幕上。为了讲解方便我们将每一行的开头用序号标出,要注意的是这些序号不属于编写程序本身,只是为了方便说明而添加上去的。# include ①文件包含void main() ②主函数{ ③程序开始int a ,b ,sum; ④变量定义scanf( "%d", &a ); /*输入a*/ ⑤格式输入函数与注释scanf( "%d", &b ); ⑥格式输入函数sum=a+b; /*对a、b求和*/ ⑦求和与注释printf("sum=%d \n",sum); ⑧格式输出函数} ⑨程序结束

我们用序号将各行语句的作用都标注出来,下面将对C语句的结构和作用进行逐一的讲解。1.文件包含

# include是文件包含,通用的格式是# include<文件名>或# include“文件名”,它属于预处理命令中的一种。文件包含的作用是将该程序编译时所需要的文件复制到本文件,再对合并后的文件进行编译。stdio.h是基本输入/输出的头文件,在上例中,我们用到输入/输出函数printf()、scanf(),因此需要在源程序的开头写上# include

在编写程序的时候,常会用到不同的函数,如sin()、cos()、abs(),而这些函数虽然已经编好,但需要提供有关信息,这就需要用到这些预处理命令,且要放在程序的开头,故也称为头文件。当用到数学函数,如sin()、cos()、abs()时,则需要写#include。【实例2-1】输入一个正整数求其平方根。#include #include void main(){float a,b; /*定义两个实数型变量a、b*/scanf("%f",&a); /*输入a的值*/a=fabs(a); /*保证被开方数为正数*/b=sqrt(a); /*对a进行开平方,并将其赋予b*/printf("%f",b);}

程序运行时,当输入:4

屏幕上输出的结果为:4的平方根是2

在【实例2-1】中,程序编译时用到了sqart()函数,因此在源程序的开头将该函数所在的头文件引入。2.主函数

main()表示主函数,这是系统提供的特殊函数,每一个C语言程序有且只有一个main()函数。函数的内部用一对大括号括起来,括起来的部分称为函数体,上例中函数体一共包括五条语句,一个C程序执行是从第1个大括号开始,到另一个大括号结束。在上面的例子中,③⑨表示的是main()函数的开始与结束,同时它也表示源程序的执行的开始与结束,这两个大括号必须配对使用,不能省去。④⑤⑥⑦⑧构成main()函数的函数体,④是函数体的说明语句,⑤⑥⑦⑧是执行语句。每一个语句后面都要以分号结尾,分号是语句结束标志。

不管main()函数在源程序中的位置在何处,执行完主函数中的所有语句后,程序就结束了。【实例2-2】程序的执行过程示例。#include int c , a=4 ; /*定义两个整型变量c、a,并对a赋值*/int func(int a , int b) /*定义func()函数,函数值为整数,a,b为形式参数*/{c=a*b ;a=b-1 ;b++ ;return (a+b+1) ;}void main(){int b=2 , p=0 ; c=1 ; /*定义三个整型变量b、p、c,并进行赋值*/p=func(b , a); /*调用func()函数*/printf("%d,%d,%d,%d\n", a,b,c,p) ;}

程序运行时,屏幕上输出的结果为:4,2,8,9。【实例2-2】中,源程序的执行不是从开头进行的,而是从main()函数所在的位置开始的,func()函数是用户自定义函数。程序从main()函数开始执行,执行到main()函数中的第四行时,开始调用func()函数,程序转到func()函数,执行完func()函数中的所有语句后,再返回到主函数func()函数中继续执行。3.变量的定义

一个变量在内存中占据一定的存储单元,在该存储单元中存放变量的值。本行定义了三个变量a、b、sum,分别用来存储等待输入的两个整型数和它们的和,便于以后的操作。

C语言中,变量的定义必须符合标识符的命名规则,即标识符只能由字母(大小写均可)、数字和下画线3种字符组成,第1个字母不能是数字。

C语言对大小写严格区分,变量一般用小写。变量遵循先定义后使用的原则,定义变量有利于系统分配存储空间,定义变量其实就是在内存中开辟存储单元。4.格式输入与输出函数

C语言本身不提供输入/输出语句,输入与输出操作是由函数来实现的。在C标准函数库中有一些输入/输出函数,可以在程序中直接调用,如printf()函数和scanf()函数,它们不是C语言文本的组成部分,而是以函数库的形式存放在系统之中。

输入函数的作用是将输入设备(如键盘)按指定的格式输入一组数据,赋给指定的变量存储单元,作为变量的值。⑤⑥两行是C语言提供的标准输入函数。&a,&b中的“&”表示“地址”,运用输入函数分别从外部输入设备输入两个整型数,存放在a、b所代表的存储单元里面。⑤⑥⑧中的“%d”是输入的“格式说明”,用来指定输入的数据类型和格式(详见第3章),“%d”表示“十进制整数类型”。

输出函数的作用是向系统指定的输出设备(如显示器)输出若干个任意类型的数据。本例中,将输入的两个整型数求和后存放在sum所代表的存储单元中,用输出函数输出到屏幕上。5.注释部分

例子中的⑤⑦行“/*”开头到“*/”结尾之间的内容表示注释,它可以在一行书写或分多行书写,可写在程序的任何位置。为了便于理解,我们常用汉字表示注释,当然也可以用英文或汉字拼音作注释。注释是程序员对程序某部分的功能和作用所做的说明,是给人看的,对编译和运行不起作用。

⑤⑦中的注释部分是对所要进行的操作的说明,⑤是一个输出语句,输入变量a的值,⑦是一个赋值语句,将a、b相加的和赋给sum。在程序中添加注释语句可以提高程序的可读性,具有提示的作用,也便于不同程序员之间的交流。

根据上面一个简单的实例,我们对C语言程序的结构做了一些简单的解释,在以后的章节中我们将对各个部分逐一进行详细的讲解。

2.2 C语言程序的书写风格

为了增强程序的可读性,便于人们理解和查错,建议使用良好的书写格式。这些书写格式有些是强制性的,有些是建议性的。怎样的才算是良好的书写格式呢?我们从下面这个实例说起。#include void main( ){int k=0; char c='A'; /*定义一个整型变量,一个字符变量,并赋值*/do { /*直到型循环*/switch (c++){ /* switch 多分支语句*/case 'A': k++; break;case 'B': k——;case 'C': k+=2; break;case 'D': k=k%2; break;case 'E': k=k*10; break;default: k=k/3;}k++;}while(c<'G');printf("k=%d\n", k);}

从这个实例中,程序里面的成分结构可以一目了然,上述程序表现了C语言的书写格式,具体如下: C语言程序使用英文小写字母书写。大写字母一般用于符号常量

或特殊用途。C语言区分字母大小写,如student和STUDENT是

两个不同的标识符。 标识符是用于标识某个量的符号,可由程序员任意定义,但为了

增加程序的可读性,命名应尽量有相应的意义,以便阅读理解及

程序员之间的交流。 不使用行号,通常按语句的顺序执行。前面的例子中我们使用编

号是为了讲解方便,在正常的源程序中,不能使用行号。 所有语句都必须以分号“ ;”结束,作为语句之间的分隔符。 C程序中一个语句可以占多行,一行也可以有多个语句,但要用

分号分隔开。 不强制规定语句在一行中的起始位置,但同一结构层次的语句应

左对齐。低一层次的语句或说明可比高一层次的语句或说明缩进

若干格后书写,以便看起来更加清晰,增加程序的可读性。属于

同一模块时要用“{ }”括起来,如上例中的do-while语句和

switch语句。 为了使程序更加清晰,可以使用空行,空行不影响程序的执行,

但不要在一个语句内加空行。 C语言中有的符号必须配对使用。如注释符号“/* */”,模块起止

符号“{ }”,圆括号“(  )”等。在输入时为了避免忘记,可

连续输入这些起止符号,然后再在其中插入代码来完成内容的编

辑。 在源程序中,凡是用“/*”和“*/”括起来的文字都是注释。可

以在程序的任何一处插入注释。注释是对程序或其局部的说明,

不参加编译也不在目标程序中出现。建议多使用注释信息,可以

增加程序的可读性。

在编程时应力求遵循这些规则,以养成良好的编程风格。

2.3 C语言程序的开发过程

在本章的第一节我们了解到计算机语言的发展过程及几种语言的区别。我们知道计算机只能识别和执行由0、1组成的二进制指令即机器语言,不能识别和执行用高级语言编写的指令即源程序。为了让计算机能执行高级语言编写出来的源程序,必须通过一定的转换过程,将源程序“翻译”成计算机能懂的机器语言,如图2-1所示。图2-1 C语言程序的“翻译”过程

把高级语言翻译成机器语言的过程称为“编译”。对C程序来说,先要通过“编译程序”将源程序翻译成二进制形式的“目标程序”,然后再通过“链接程序”将该目标程序与系统的库函数及其他目标程序链接起来,形成可执行目标程序。

一般来讲,C程序开发过程要经历创建源程序、编译源程序、链接目标代码、运行可执行文件等过程,如图2-2所示是4个基本步骤。图2-2 C语言程序的开发过程1.编辑

编辑创建源程序是将编写好的C语言源程序代码录入到计算机中,形成源程序文件。编辑器可以是计算机所提供的某种文本编辑软件,也可以是C系统提供的编辑器。源程序编辑好后以文本文件的形式保存到磁盘中,本书使用Visual C++ 6.0运行环境,保存文件的扩展名为“.cpp”,源程序文件名由用户自己选定,例如“student.cpp”。2.编译

编译的功能就是调用“编译程序”将已编辑好的源程序翻译成二进制的目标代码。系统对源程序进行编译时,还对源程序的语法进行检查。当发现错误时,会在屏幕上列出错误的位置及种类,此时要再次使用编辑工具对源程序进行排错修正,如果源程序没有语法错误,编译后将产生一个与源程序同名,以“.obj”为扩展名的目标程序。例如,编译源程序为“student.cpp”,将产生目标程序“student.obj”。3.链接

编译后产生的目标程序不能直接用于运行,因为每一个模块往往都是单独编译的,需要把各个模块编译后得到的目标程序及系统提供的标准库函数等链接后才能运行。链接过程是使用系统提供的“链接程序”进行的,链接后产生以“.exe”为扩展名的可执行目标程序。例如编译源程序为“student.cpp”,编译链接后生成“student.exe”。4.运行

可执行目标程序生成后,就可以在操作系统的支持下运行。若执行结果达到预期的目的,则开发工作到此完成。否则,要进一步检查修改源程序,再经过“编辑—编译—链接”的过程,直到取得正确的运行结果为止。

本书使用Visual C++ 6.0集成开发环境。为了帮助读者更好地理解C语言开发的过程,我们在这里以“student.cpp”为例,将它的开发过程以流程图的形式展现出来,运行过程一目了然,如图2-3所示。图2-3 C语言程序开发流程图

2.4 Visual C++集成开发环境

在了解C语言的初步知识之后,本节要讲解一下C语言的集成开发环境——Visual C++ 6.0。Visual C++ 6.0是一个基于Windows操作系统的可视化集成开发环境(Integrated Development Environment,IDE),已成为专业程序员进行软件开发的首选工具,是目前非常盛行的一种C编译系统,功能十分强大,操作方便,视图界面友好。2.4.1 熟悉Visual C++ 6.0集成开发环境

为了帮助大家熟练运用Visual C++ 6.0进行C语言开发,在这一部分我们熟悉Visual C++ 6.0集成开发环境的界面及构成。1.安装Visual C++ 6.0

Visual C++ 6.0过程不是非常复杂,只需要运行安装文件中的setup.exe程序,然后按照安装程序的提示信息进行操作,可以指定系统文件存放的路径,但一般不必自己另行指定,采用系统提示的默认方案即可完成安装过程。2.启动Visual C++ 6.0

在【开始】菜单中的【程序】选项的Microsoft Visual Studio 6.0级联菜单下,选择Microsoft Visual C++ 6.0,启动Visual C++ 6.0,进入Visual C++ 6.0的界面。或者双击安装过程中建立在桌面的快捷图标,也可以启动Visual C++ 6.0。3.Visual C++ 6.0的工作界面

Visual C++ 6.0的工作界面如图2-4所示。图2-4 Visual C++ 6.0主窗口 标题栏位于主窗口的最上方,它显示了应用程序的标题名称。 菜单栏除包含常用的文件操作以外,还包含了程序开发过程中所

需要的各种操作。 工具栏是一种图形化的操作界面,将菜单栏中的常用项以快捷的

方式显示出来,熟练掌握工具栏的使用方法以后,工作效率将会

大幅提高。 编辑区用来对源文件、资源文件、文档文件等进行编辑,现在的

编辑区是灰色的,表示还没有文件在进行编辑。 工作区(Workspace)用来管理工程的一些信息,包括类、工程

文件、资源等,各种文件的类属很清晰,可以有条不紊地进行各

种信息的编辑。 输出区(Output)包括编译、调试、查找等信息的输出,这些输

出信息以多页面的方式出现在输出区中,例如,在对工程进行编

译和链接后,如果程序有错误或警告,则显示在输出区,可以对

照错误或警告提示进行程序修改。 状态栏用来显示界面当前所处的状态。2.4.2 C语言在Visual C++ 6.0的开发过程

如图2-4所示,刚开始进入Visual C++ 6.0的界面时,里面的项目工作区和文本编辑区是空的,要开始一个新程序的开发,需要通过应用程序向导建立新的工程项目,并在项目中添加文件,然后再进行其他的开发操作。1.新建工程项目

项目也称工程,Visual C++ 6.0用工程化和管理方法把一个应用程序中的所有相互关联的一组文件组织成一个有机的整体,项目以文件夹方式管理所有源文件,项目名作为文件夹名。具体过程如下:(1)单击Visual C++ 6.0主窗口菜单栏中的“文件”菜单选项。(2)单击下拉菜单中的“新建”命令,弹出“新建”对话框,如图2-5所示。图2-5 “新建”对话框(3)在“工程”选项卡下,选择“Win32 Console Application”选项,在“工程名称”一栏中输入项目名称,如c_example,“位置”一栏用来设置新建项目的存储位置,一般情况下为默认位置,不作设置,然后单击“确定”按钮。(4)进入“Win32 Console Application步骤”对话框,如图2-6所示,选择你想要创建的控制台程序,默认情况下为创建一个空工程,然后单击“完成”按钮。图2-6 “Win32 Console Application步骤”对话框(5)显示“新建工程信息”对话框,给出你所创建工程的信息,如图2-7所示,单击“确定”按钮。图2-7 “新建工程信息”对话框(6)系统自动返回Visual C++ 6.0的主窗口界面,此时项目工作区已经显示了你所创建的项目内容。2.建立项目中的文件

若要在新建立的项目中创建文件,可以打开相应的项目文件,如刚才我们创建了一个名称为c_example的项目,里面没有任何文件,我们现在在里面创建源程序文件或头文件,具体操作如下:(1)选择“文件”菜单下的“新建”命令,在弹出的“新建”对话框中,选择“文件”选项卡,如图2-8所示。(2)选择想要创建的文件类型。这里我们选择“C++ Source File”选项,在“文件名”一栏中输入创建文件的名称,如图2-8所示,然后单击“确定”按钮。图2-8 “新建”对话框(3)此时系统自动返回Visual C++ 6.0的主窗口界面,并显示刚才建立的文件编辑区窗口,在文件编辑区可以对源文件或头文件的内容进行编辑操作。3.文件保存

编辑完源文件后,需要对其进行保存,有两种方法:(1)选择“文件”|“保存”命令。(2)单击工具栏中的保存按钮。

2.5 用Visual C++ 6.0运行一个C程序

在上一节我们熟悉了Visual C++ 6.0集成开发环境,以及其中的程序开发过程,现在我们来编辑并运行一个简单的C语言程序,熟悉Visual C++ 6.0中的整个上机过程。1.编辑源程序

这里我们使用上一节建立的c_example工程,在其里面再建立一个名称为example的C++ Source File文件。进入文件编辑区输入以下程序代码:#includevoid main( ){int a,b,sum;printf("输入第1个数a:");scanf( "%d", &a ); /*输入a*/printf("输入第1个数b:"); /*输入b*/scanf( "%d", &b );sum=a+b; /*对a、b求和*/printf("%d和%d的和是%d \n",a,b,sum);}2.编译链接源程序

源程序编辑完成之后,要进行编译、链接生成可执行目标代码文件。具体的操作过程如下:

选择菜单栏中的“组件”下拉菜单下的“编译”命令(或按Ctrl+F7组合键)进行编译,然后选择菜单栏中的“组件”下拉菜单下的“组件”(F7键为快捷键命令进行链接),系统将会在输出窗口给出所有的错误信息和警告信息。当所有错误修正之后,系统将会生成扩展名为.exe的可执行文件。对于输出窗口给出的错误信息,双击可以使输入焦点跳转到引起错误的源代码处以进行修改。

另一种方法是:可以先单击主窗口工具栏上的编译按钮进行编译,再单击主窗口工具栏上的Build按钮进行链接。3.运行程序

选择菜单栏中的“组件”下拉菜单下的“执行”命令(Ctrl+F5组合键为快捷键)运行程序,或者单击工具栏上的运行按钮来执行编译链接后的程序。运行时,将会出现一个DOS窗口,按照程序输入要求正确输入数据。程序运行成功时将会在屏幕上输出执行结果。本源程序运行的结果如图2-9所示。图2-9 程序运行结果

2.6 本章小结

本章通过对一个C语言程序实例进行分析,介绍了C语言程序的基本结构特点、正确规范的书写风格,在此基础之上认识了C语言程序的开发过程,并讲解了Visual C++ 6.0的运行环境,用Visual C++ 6.0运行一个具体的C语言程序实例,帮助读者熟练掌握C语言的开发过程。

2.7 习题

1.C语言的结构特点有哪些?

2.怎么使用注释语句?使用注释语句有哪些好处?

3.简述C语言程序在Visual C++6.0中的开发过程。

4.模仿例题,编写一个C语言程序,并上机调试。程序的输出结果为:***********hello world!***********

第2篇 C语言基础

第3章 常量、变量与标识符

C语言中的数据包括常量和变量,作为操作对象的数据都是以某种特定的形式存在的,可以用C语言中的标识符来表示一个常量或者一个变量。 标识符; 常量; 变量; 变量的初始化。

3.1 标识符

我们已经知道在C语言中,数据是在计算机内存中存储的,程序设计中用到的数据,要到计算机的内存中读取,因此需要用到一个符号来代表它,这就是我们所要讲的标识符。

标识符是指用来标识常量名、变量名、函数名、数组等对象,按照一定的命名规则定义的字符序列,即一个代号。3.1.1 标识符的命名

标识符的命名规则如下: 标识符由字母(包括大写字母和小写字母)、数字及下画线组成,

且第1个字符必须是字母或者下画线。 在C语言中,大写字母和小写字母是有区别的,即作为不同的字

母来看待,应引起注意。

下面是合法的C语言标识符:

A_3、home、student_name、_file、Teacher、TEACHER

下面是不合法的标识符: A=2:标识符中出现非法字符“=”。 3b:数字不能作为标识符的第1个字符。 Student name:空格不能出现在一个标识符的中间。3.1.2 保留字

保留字也称关键字,是指在高级语言中,那些已经定义过的标识符,用户不能再将这些字作为变量名、常量名、函数名、数组名等。

C语言共有32个关键字,具体可分为4类。 数据类型关键字(12个):char、double、enum、float、int、

long、short、signed、struct、union、unsigned、void。 控制语句关键字(12个):break、case、continue、default、

do、else、for、goto、if、return、switch、while。 存储类型关键字(4个):auto、extern、register、static。 其他关键字(4个):const、sizeof、typedef、volatile。

C语言中除了上述的保留字外,还使用一些具有特定含义的标识符,称为特定字。如include、define、ifdef、ifndef、endif、line。这些特定标识符主要用在C语言的编译预处理命令中。

在C语言中,标识符的命名除了遵守命名规则、不使用关键字以外还要注意以下几点。 在C语言中,大写字母和小写字母是有区别的,即作为不同的字

母来看待,因此Teacher、TEACHER是两个不同的标识符。 在起名时,应注意做到“见名知义”。比如表示姓名,比较好的

标识符:Name、name、xing_ming、Xingming、xm等;比较差

的标识符:x、y、abc等。 尽量不用单个的“l”和“o”作为标识符。这个与数字中的“1”

和“0”很相像,程序设计过程中容易混淆。 数学计算时可以采用习惯的名字。如圆的半径和面积:r,s;立

方体的长、宽、高和体积:a、b、h、v。

3.2 常量

常量是指在程序运行过程中其值不随程序的运行而改变的量。常量在程序中不需要进行任何说明就可以直接使用,它本身就隐含了它的类型。常量分为直接常量和符号常量。3.2.1 直接常量

直接常量是直接写出来的,直接常量的书写形式决定了它的类型。直接常量包括整型常量、实数型常量、字符型常量和字符串常量。例如, 整型常量:15、-8、0。 实数型常量:3.7、-8.2、58.12E-2。 字符常量:‘a’、‘A’、‘+’、‘5’。 字符串常量:“this is a boy.”、“a”、“123”。3.2.2 符号常量

符号常量是指用一个标识符代表一个常量。如商场内某一产品的价格发生了变化,如果在一个程序中多次用到了这种商品的价格,逐一修改非常麻烦,这时可以定义一个符号常量,在文件的开头写这么一行命令:#define PRICE 50

这里用#define命令行定义PRICE代表常量50,后面的程序中用到这种商品的价格时,直接用PRICE,可以和常量一样进行运算。如果常量的值需要发生变化,那么只需要在#define命令行进行修改,达到一改全改的目的。

这里需要说明以下几点: 符号常量名习惯上用大写,以便与变量名相区分。 一个#define对应一个常量,占一行;有n个常量时需n个#define

与之对应,占n行(这将在第7章的预编译部分进行详细的讲

解)。 符号常量不同于变量,它的值在其作用域内不能改变,也不能再

被赋值。 在程序中使用符号常量具有可读性好、修改方便的优点。【实例3-1】符号常量使用举例。#include#define WHY "I am a student."void main( ){printf( "I am a student.\n" );printf( "%s \n", "I am a student." );printf( "%s \n", WHY );}

程序的运行结果如图3-1所示。图3-1 程序运行结果

3.3 变量

变量是指在程序运行过程中其值可以改变的量。程序中使用的变量名是用户根据需要而取名的,变量名必须符合标识符的命名规则。

在C语言中,由于程序的多样性的需要,对变量也有各种各样的要求,比如,变量的生命期,变量的初始状态,变量的有效区域,变量的开辟地和变量的开辟区域的大小等。为了满足这些要求,C语言设置了以下变量:不同数据类型的变量、全局变量、局部变量、静态变量(静态全局变量和静态局部变量)、寄存器变量、外部变量等。这里只讲解不同数据类型的变量,在第6章将逐一对其他种类的变量进行讲解。3.3.1 变量的定义

在C语言程序中,常量可以不经说明而直接引用,而变量则必须遵守“先定义,后使用”的原则。凡未被定义的,不能作为变量名,这就能保证程序中变量名使用正确。变量定义的语法为:数据类型 变量名;

数据类型是C语言中合法的数据类型,包括整型、实数型、字符型等。

变量名是C语言中的合法标识符。这里的变量名可以是一个,也可以是多个。如果是多个变量名,彼此之间要用逗号分开,表示同时定义若干个具有相同数据类型的变量。例如:int a; /*定义整型变量a*/char ch1,ch2,ch3; /*定义一个字符型变量ch1,ch2,ch3*/

定义变量时需要注意以下几点。 每个变量定义语句都必须以分号结尾。 变量定义语句可以出现在变量使用之前的任何位置。程序设计时

只要不违背“先定义,后使用”的原则即可。 变量一经定义,每个变量就有一种确定的类型,在编译时就能为

其分配相应的存储单元。 一个变量在内存中占据一定的存储单元,用变量名来标识在内存

中所分配的存储单元,在该存储单元中存放变量的值。3.3.2 变量初始化的方法

变量的初始化就是对变量赋初值。初始化变量并不是必需的,但是在C语言中未初始化的变量是其数据类型允许范围内的任意值(静态变量除外)。为了防止运算中出错,一般建议定义变量后,立即初始化。变量的初始化有两种方法:一种是定义初始化,即定义变量的同时对其赋予初始值。例如:int a=10;char c=’V’;float p=15.36;

另一种方法是先定义变量,然后再进行赋值或是等到需要赋值的时候再赋值。例如:double a,b,c;a=1.2;b=3.00;c=3.14159;

3.4 变量的初始化

在3.3节中已经初步介绍了变量及其初始化,变量的初始化就是对变量赋予一定的初值。例如:int x,y,z=1;x=4;y=2;

上例中,定义了x、y、z三个整型变量,变量z在定义的时候赋予了初始值,即定义初始化。而x、y在定义的时候并没有对其初始化,而是用赋值语句对其进行赋值。

对于变量的初始化,可以归纳出以下几点:(1)初始化实际上是一个赋值语句。(2)在定义变量的时候,可以只给部分变量赋值。例如:char ch1,ch2=’a’,ch3=’b’,ch4;

这条语句定义了4个字符型变量,并给ch2、ch3赋了初值,而ch1、ch4没有赋值。(3)如果同时对几个变量赋相同的初值,应该注意书写格式。int a=1,b=1,c=1;

这条语句定义了三个整型变量,同时赋相同的初值1,但是不能写成以下的格式:int a=b=c=1;

而且几个变量之间用的是逗号,不是分号。如果是分号,相当于只定义了整型变量a,并对其赋了初值,而变量b,c没有定义,但是赋了初值,这违反了变量“先定义,后使用”的原则,程序在运行的过程中会出现错误。

在C语言中使用变量时,如果它出现在表达式中,事先必须有一个初始值,否则其值将是一个不确定的值。变量获取初始值有以下几种方法。 赋值语句:“=”在C语言中是赋值符号,运用赋值符号可以对变

量进行赋值。例如:ch=’A’。 读取语句:有些程序的值是不确定的,需要用户自己输入,因此

需要用读取语句从外部输入。例如:int y;scanf("%d",&y);

先定义一个整型变量,然后使用标准输入语句,由用户决定变量的值。

3.5 本章小结

本章主要讲解了标识符、常量和变量。标识符是用来标识变量名、符号名、函数名、数组名或文件名的一些具有专门含义的名字,如a,_12。常量也称为常数,在程序运行过程中其值不能被改变,如1.2,0xab。常量标识符通常大写。变量相对于常量,其值在程序运行过程中可以被改变。变量标识符通常小写,关键字不能作为变量名。对变量赋予一定的初值称为变量的初始化。

3.6 习题

1.标识符的命名规则有哪些?

2.什么是保留字?

3.常量和变量有什么区别?

4.下列数据中属于“字符串常量”的是哪一个?

"a" {ABC} ‘abc\0’ ‘a’

5.在计算机中,"a\xff"在内存中占用多少字节数?为什么?

第4章 数据类型

C语言具有丰富的数据类型。C语言中的数据类型分为四大类,即基本数据类型、构造类型、指针类型和空类型。本章重点介绍了基本数据类型。 C语言中的数据类型; 整型数据; 实数型数据; 字符型数据; 数值型数据间的混合运算。

4.1 C语言中的数据类型

所谓数据类型,是按被说明量的性质、表示形式、占据的存储空间的多少、构造特点来划分的。在C语言中,数据类型可分为基本数据类型、构造数据类型、指针类型、空类型,如图4-1所示。图4-1 C语言中的数据类型结构图

数据的类型不同,它们的取值范围、运算属性及存储方式都会不相同,C语言程序中所用到的数据都必须指明一定的数据类型后才能对数据进行各种操作。4.1.1 基本数据类型

基本数据类型是语言系统定义的数据类型,只能有单一的值,在程序定义变量时可以直接引用。C语言中常用的基本数据类型有整型、实数型、字符型。如在填写人的年龄时,使用整型数据;学生的分数要用实数型类型;学生姓名是由多个字符组成的。4.1.2 构造数据类型

构造数据类型是由基本数据类型按一定的规则组合而成的,因此也称为导出类型数据。数组是由相同类型的数据组合而成的,如将一个班级学生的数学成绩组合在一起,就是一个实数型数组。结构体是由不同类型的数据组合而成的,比如统计一个学生的信息包括学号(长整型)、学生姓名(字符型)、性别(字符型)、年龄(整型)等,所有的数据组合在一起就成了构造体。当若干个数据不同时使用时,为了节省内存空间,我们就可以让它们占用相同的内存区域,这些数据组合起来就是共用体,它可以是同类型的数据,也可以是不同类型的数据。4.1.3 指针数据类型

指针是一种特殊的数据类型,是C语言的核心,也是C语言重点所在,同时又是具有重要作用的数据类型,其值用来表示某个量在内存储器中的地址。在本书的第14章我们将会进行重点讲解。4.1.4 空类型

空类型是从语法完整性的角度给出的一种数据类型,表示不需要具体的数据值,因此也就没有数据类型。空类型在调用函数值时,通常应向调用者返回一个函数值,这个返回的函数值是具有一定的数据类型的,应在函数定义及函数说明中给予说明,例如在本书第2章例题【2-2】中定义的int func(int a , int b)函数,其中“int”类型说明符即表示该函数的返回值为整型量。但是,也有一类函数,调用后并不需要向调用者返回函数值,这种函数可以定义为“空类型”,其类型说明符为void。

在计算机中每种数据都要在内存中分配若干个字节,用于存放该数据,不同类型数据的长度是不同的,因此在使用任何一个数据之前,必须对数据的类型加以定义,以便为其安排长度合适的内存。本章将重点介绍基本数据类型,其他复杂的数据类型将在以后的章节中逐一介绍。

4.2 整型数据

整型数据分为一般整型、短整型和长整型,并且每一种类型又分为带符号和无符号两种类型。

如表4-1所示,整型数据中一般整型、短整型和长整型的带符号数和无符号数的取值范围以及在内存中所占用的字节数是不同的,这与它们所运行的环境是有关系的,而表4-1中使用的是Visual C++ 6.0的开发环境,如果用的是Turbo C 2.0环境,整型在内存中占两个字节数,取值范围为-32768~32767,带符号数的取范围为-32768~32767,无符号数的取值范围为0~65535。表4-1 整型数据4.2.1 整型常量

整型常量的数据类型是整数,包括正整数、负整数和零。在C语言中,整型常量有以下三种不同的数制表示形式。 十进制整数常量:这种表示方法就是我们平时所熟悉的表示方

法,由数字0~9构成,最高位也就是左边第一位不能为0。例

如-39、0、171等。 八进制整型常量:以数字0开头,其后再写上要表示的八进制数。

八进制数各位由0~7这八个数字之一组成。例如0134、

0471、-072。 十六进制整型常量:以0X或0x开头,其后再写上要表示的十六

进制数。十六进制各位由数字0~9或字母a~f或A~F构成。如

0x17、0XCF,-0X1f等。

C语言中提出长整型常量是为了扩大整型数据的数值范围,书写方式也分为十进制、八进制和十六进制,唯一不同的是在整数的末尾要加上大写字母“L”或小写字母“l”。例如,18L、-023L、+0x3BL都是长整型常量。需要注意的是16与16L虽然数值相同,但是它们是不同的整型常量。另外,各个进制都有正负之分,正数前面的“+”号是可以省略的。4.2.2 整型变量

前面我们了解到,变量的定义由数据类型和变量名组成,数据类型不同变量的类型也就不同,有整型变量、实数型变量、字符型变量等。

整型变量是指其值为整型数据的变量。整型数据有三种,即整型(int)、短整型(short int)和长整型(long int)。为了方便书写,我们将short int和long int后面的int省略,分别用short和long来表示短整型和长整型。1.整型变量的定义

整型变量分为整型变量、短整型变量、长整型变量。例如:int a; /*定义一个整型变量a*/short d=16; /*定义一个短整型变量d*/long s; /*定义一个长整型变量s*/

整型数据分为带符号数和无符号数,定义时在前面加上signed为带符号变量,加上unsign为无符号变量,因此整型变量归纳起来共有6种变量类型。通常情况下,我们定义的整型变量都是没有符号标识符的,默认为是带符号变量,只是省略了signed而已,因此上面定义的三个变量实际上为:[signed] int a; /*定义一个带符号整型变量a*/[signed] short d=16; /*定义一个带符号短整型变量d,并赋值为16*/[signed] long s; /*定义一个带符号长整型变量s*/

定义无符号整型变量如下:unsigned [int] num; /*定义一个无符号整型变量num*/unsigned short b; /*定义一个无符号短整型b*/unsigned long count=429496720; /*定义一个无符号长整型数count,并赋值为429496720*/2.整型变量的简单运用

不同类型的整型数据可以进行算术运算,下面我们来看一个简单的实例。【实例4-1】整型数据简单运算。#includevoid main(){int a,b,c; /*定义三个整型变量a,b,c*/unsigned u; /*定义一个无符号整型变量u*/a=12;u=8; /*对变量c和u进行赋值*/b=a-24;c=b+u;printf("b=a-24=%d,c=b+u=%d\n",b,c);}

程序运行的结果为:b=a-24=-12,c=b+u=-4,如图4-2所示。图4-2 程序运算结果

此例只是将整型数据与无符号数据进行运算,实际上整型与短整型和长整型之间也可以进行运算,但涉及类型之间的转换,我们将在数值型数据间的混合运算一节中进行详细的讲解。

从表4-1中我们知道不同的整型数据有自己不同的取值范围,例如short int的值是在-32768~32767之间。如果我们将最大值加1或者最小值减1会产生什么情况呢?【实例4-2】整型数据的溢出。#include void main(){short a,b,c,d;a=32767;b=a+1;c=-32768;d=c-1;printf("%d\n%d\n%d\n%d\n",a,b,c,d);}

程序运行结果如图4-3所示。图4-3 程序运行结果

从例4-2我们可以看到,当数据超出数据类型的取值范围时就会产生数据溢出,遇到这种情况程序在运行过程中并不会出错,好像汽车里程表一样,达到最大值以后又从最小值开始计数。因此,在给变量赋值及进行数值运算的时候要注意变量数据类型的取值范围,防止因出现数据的溢出现象而得不到正确的结果。

4.3 实数型数据

实数型数据表示的实际上就是带小数的数值,又称为浮点型数据。实数型数据分为单精度实数型(float)、双精度实数型(double)和长双精度实数型三种,长双精度实数型数据一般情况下很少用到。它们表示数值的方法是一样的,区别在于数据的精度、取值范围以及在内存中占用的存储空间有所不同,如表4-2所示。表4-2 实数型数据

如表4-2所示,不同类型的实数型数据有效数字不同。例如,实数123456789在单精度实数型数据的取值范围内,有效数字为7~8个,但它的有效数字超过了8个,如果将它赋给一个单精度实数型变量,该数的最后一位就失去了有效数字,变成了一个随机数,降低了精度。4.3.1 实数型常量

在程序运行过程中不能被改变其值的实数型数被称为实数型常量。实数型常量在C语言中又称为浮点数。实数型常量有两种表示形式。1.小数表示法

C语言中实数只能使用十进制小数表示,不能用八进制或十六进制表示。这种形式由符号、整数部分、小数点和小数部分组成,其格式如下:±整数部分.小数部分

其中整数部分或小数部分允许省略,但不能同时省略,即“14.0”可以写成“14.”或“14”;“0.15”可以写成“.15”,而“0.0”不能写成“.”。数前面的“±”表示数的符号,“+”表示数为正数,可以省略,“-”表示数为负数,不能省略。小数点是小数部分的标志,不能省略。例如25.6、-67.15、-.0014、0.48,这些都是正确的小数形式的实数。2.指数表示法

用指数形式表示特别大或特别小的数值。指数形式的实数由尾数部分、字母E或e和指数部分组成。其格式如下:±尾数部分E(e)±指数部分

其中尾数部分是十进制实数,指数部分是十进制短整型常量。尾数前面的“±”决定这个数的正负,后面的“±”决定指数的大小。指数部分只能是整数,并且指数形式的三个组成部分都不能省略。例如5.154E-12、54.12e+0.5、0.12e-25、-21.563e9,这些都是正确的指数形式的实数。

指数形式的表示方法实际等价于:±指数部分±尾数部分*1035

因此,12.3e3等价于12.3*10,0.12E+5等价于0.12*10。

计算机在用指数形式输出一个实数时,是按规范化的指数形式输出的。所谓规范的形式即在字母E或e的尾数部分中,小数点左边应有且只有一位非零的数字。例如,10023.45可以表示为0.1002345e+5、1.002345e+4、10.02345e+3等,其中只有1.002345e+4才是规范化的指数形式。需要说明以下几点: 实数型常量的类型都是双精度浮点型。 实数在计算机中只能近似表示,运算中也会产生误差。 小数部分和指数部分具体有多少位,没有具体的标准,不同的编

译系统有不同的规定。小数部分越多,精确度越高;指数部分越

多,数值的范围就越大。4.3.2 实数型变量

在程序运行过程中可以改变其值的实数型数被称为实数型变量。实数型变量分为单精度(float)、双精度(double)和长双精度三种类型。在定义实数型变量时用以下方式:float x; /*定义float型变量x*/double y; /*定义double型变量y*/long double z, /*定义long double型变量z*/

对于实数型常量不区分float型和double型。一个实数型常量可以赋给一个float型或double型变量。根据变量的类型截取实数型常量中相应有效数字,在有效位以外的数字将被舍去,因此会产生一定的误差。【实例4-3】测试单精度实数型的有效位数。#include void main(){float x; /*定义float型变量x*/x=7.123456789; /*对float型变量x赋值*/printf("%12.10f",x); /*以总长度为12,小数点位数占10位的形式输出x*/}

程序运行的结果为:7.1234569550。由此可以看出,float型的数据只接收到7位有效数字,后面的数字是一些无效的数值。但是在很多时候,虽然数字在浮点数表示的范围之内,但是由于有效数字的限制,也会产生误差。

4.4 字符型数据

字符型数据由字母、符号和不用于算术操作的数字组成,又称为非数值型数据。字符型数据分为字符型(char)、带符号字符型(signed char)和无符号字符型(unsigned char),如表4-3所示。表4-3 字符型数据

在附录AASCII字符集中列出了所有可以使用的字符,每个字符在内存中占用一个字节,用于存储它的ASCII码值,所以在C语言中,字符具有数值的性质,带符号字符与无符号字符能够参与到整型数据的运算当中。字符参与运算相当于对字符的ASCII码值进行运算。4.4.1 字符型常量

字符型常量包括由一对单引号括起来的一个字符构成的一般字符常量和由反斜杠(\)开头的特定的字符序列构成的转义字符。1.一般字符常量

字符型常量是由一对单引号括起来的一个字符。这个字符是ASCII字符集中的字符,字符常量的值为该字符的ASCII值。例如:'A'、'x'、'D'、'?'、'3'、'X'

这些都是字符常量,但是'x'和'X'是不同的字符常量,从ASCII字符集中可以看到,'x'的码值为88,而'X'的码值为120。

字符常量可以像整数一样参与运算,如字符'A'的码值为65,则'A'+1=66,在ASCII字符集中66对应的字符为'B',因此我们就可以这样写:'A'+1='B'。2.转义字符

转义字符是指由反斜杠(\)开头的特定的字符序列。C语言允许使用这种特殊形式的字符常量,因为在程序设计过程中,有一些字符如回车符、退格符、制表符等控制符号,不能在屏幕上显示,也不能从键盘上输入,只能用转义字符来表示,如表4-4所示。表4-4 转义字符

下面我们对表4-4中经常用到的转义字符进行解释:(1)“\n”换行符的ASCII码值为10,常在输出时用于换行。如printf("I am a student.\n");输出字符串“I am a student.”后换行,下一次输出从另一行开始。(2)“\t”水平制表符的ASCII码值为9,它的作用是将光标移到最接近8的倍数的位置,使得后面的输出从此开始。换句话说,如果所有数据都紧跟在制表符后面输出,则这些数据只能从第9列、第17列、第25列……开始。例如:printf(" abc\tde\n");

输出的结果为:abc□□□□□de(3)“\0”是空字符,它的ASCII码值为0,表示NULL,是字符串的结束标志。(4)“\ddd”表示的是斜杠后面跟着三位八进制数,该三位八制数的值即为对应的八进制ASCII码值。例如:“\101”转换为十进制数为65,在ASCII字符集表上对应的是字母'A'。(5)“\xhh”表示的是“\x”后面跟着二位十六进制数,该两位十六进制数为对应字符的十六进制ASCII码值。例如:“\x47”转换成十进制数为71,在ASCII字符集表上对应的是字母'G'。【实例4-4】转义字符应用举例。#include void main( ){printf("boy\tgirl\rj\n");}

程序运行的结果是:joy□□□□□girl4.4.2 字符型变量

字符型变量就是用一个标识符表示字符型数据,并且该标识符的值可以发生变化。字符变量只能存放一个字符。1.字符型变量的定义与存储

字符型变量就是值为字符常量的变量。字符型变量只能存放一个字符。

字符型变量的定义与整型变量、实数型变量的定义相同,如下:char c1,ch1;

它表示定义两个字符型变量c1、c2,它们可以各自存放一个字符。

字符型变量用来存放字符型常量,但它存储的不是字符本身,而是该字符对应的ASCII代码的值。例如:char ch;ch=’a’;

实际上,字符型变量ch在内存中存储的不是字符a,而是字符a对应的ASCII代码的值97,换算成二进制数为01100001,因此字符型变量ch对应的内存单元中存放的是二进制数01100001。

正是因为在内存中字符数据以ASCII码存储,所以字符的存储形式与整型的存储形式类似,这样使得字符型数据和整型数据之间可以通用。【实例4-5】字符型数据的输出。(1)以字符形式输出,代码如下:#includevoid main( ){char ch1,ch2;ch1='A';ch2='B';printf("%c,%c",ch1,ch2);}

程序运行的结果为:A,B。(2)以整型数据形式输出,代码如下:#includevoid main( ){char ch1,ch2;ch1='A';ch2='B';printf("%d,%d",ch1,ch2);}

程序运行的结果为:65,66。

从【实例4-5】我们可以看出,字符型变量既可以以字符的形式输出,也可以以整型的形式输出。以字符的形式输出时,需要先把存储单元中的ASCII的值转换成相应的字符,然后输出。以整型形式输出时,直接将ASCII码值作为整型数输出。

在C语言中允许将字符型数值赋给整型变量,也允许将整型数值赋给字符型变量。如【实例4-5】的第1个小例子中,用“ch1=65;ch2=66;”来替换“ch1='A';ch2='B';”,将会得到同样的结果。

但字符型数据和整型数据之间通用的前提是必须在合法的范围内。因为两种数据类型数值的取值范围不同。字符数据只占一个字节,把字符看成无符号数时,它只能存放0~255范围内的整数;若是有符号数,则为-128~127。2.字符型变量的简单运用

由于字符的存储形式与整型的存储形式类似,字符型数据和整型数据在一定范围内通用,因此字符型数据可以以其ASCII码值参与算术运算。【实例4-6】字符型数据的运算。#includevoid main( ){char c1,c2; /*定义两个字符型变量c1,c2*/c1=97; /*将整型数据97赋值给字符型变量c1 */c2=98; /*将整型数据98赋值给字符型变量c2 */printf("%c %c \n",c1,c2); /*将字符型变量c1,c2以字符的形式输出*/printf("%d %d \n", c1,c2); /*将字符型变量c1,c2以整型的形式输出*/c1=c1-32;c2=c2-32;printf("%c %c \n",c1,c2);}

程序运行的结果为:a b97 98A B

从上面的实例中我们可以看出以下几点:(1)在一定范围内,字符型数值可以赋给整型变量,整型数值也可以赋给字符型变量。上例中我们定义了字符型变量c1,c2,然后将整型数据97赋值给字符型变量c1,将整型数据98赋值给字符型变量c2。(2)字符型变量可以用字符形式输出(即%c),也可以用整数形式输出(即%d),但是应注意字符数据只占一个字节。(3)程序的第9行和第10行是把两个小写字母a和b转换成大写字母A和B。'a'的ASCII码为97,而'A'为65,'b'为98,'B'为66。从ASCII代码表中可以看到每一个小写字母比它相应的大写字母的ASCII码大32。C语言允许字符数据与整数直接进行算术运算,即'a'+32会得到整数97,'a'-32会得到整数65。

4.5 数值型数据间的混合运算

C语言中,一般情况下相同类型的数据可直接进行运算,运算的结果就是这种类型。例如: 5.0/2.0,参加运算的两个数都是实数型,结果为实数型2.5。 5/2,参加运算的两个数都是整型,结果为整型2。

C语言中,不同类型的数据可以混合运算。前面我们知道整型数据和字符型数据通用,而实数型数据又可以与整型数据混合运算,因此,整型、实数型、字符型数据之间可以混合运算。但是在进行运算的时候,不同类型的数据要先转换成同一类型,然后再进行运算。数据的类型转换包括自动类型转换和强制类型转换。4.5.1 自动类型转换

自动类型转换是由系统自动完成的,又称为隐式转换。不同类型的数值进行运算时,系统会自动将级别低的类型转换成级别高的类型,然后再进行运算,运算结果与其中级别高的操作数的类型相同。数据类型的自动转换需要遵循的规则如图4-4所示。图4-4 自动类型转换规则

在水平方向上,从右向左转换。所有的char型和short型自动转换成int型,所有的unsigned short型自动转换成unsigned型,所有的long型自动转换成unsigned long型,所有的float型自动转换成double型。

在垂直方向上,自下而上转换。从级别比较高的向级别比较低的方向转换。这里需要注意的是,箭头表示的是对象为不同类型的数据时转换的方向,并不表示转换的过程。例如:int i;float f;double d;long e;10 + ’a’ + i*f – d/e

该表达式的数据类型的转换过程如图4-5所示。图4-5 数据类型的转换过程

根据运算的次序,先计算i*f和d/e,分别先将int型的i转换成double型,将float型的f转换成double型,i*f的结果是double型的;将long型的e转换成double型的,d/e的结果为double型。就这样按照运算的顺序进行类型的转换,最终的结果为double型。

在赋值运算时,如果变量的类型与所赋予的变量的值不是同一类型,那么赋值号右侧表达式的类型自动转换成赋值号左侧变量的类型。例如:int a;char b;long c;c=a+b;

在进行运算时,先计算a+b,将a和b转换成int型后求它们的和,结果是int型;再将a+b的和转换成变量c的类型long,然后再赋值给c。4.5.2 强制类型转换

强制类型转换是利用强制类型转换运算符将数据类型转换成所需要的类型。强制类型转换符是由一对圆括号将某个类型名括起来构成的。

强制类型转换的语法格式为:(类型名)表达式

如:(double)a /*将变量a转换成double型*/(int)(x+y) /*将x与y的和转换成整型*/(int)x+y /*先将x转换成整型,然后再与y求和*/【实例4-7】求一个浮点数的个位数字。#includevoid main(){float x;int a;printf("Enter a float number:\n");scanf("%f",&x);a=(int)x%10;printf("a=%d\n",a);}

程序运行的结果为:Enter a float number:123.456↙a=3

求余运算符的两个操作数必须是整型数据或字符型数据,而例4-7里面的变量x定义为一个单精度浮点型数据,无法进行求余运算,因此要将其进行强制类型转换,转换成整型,然后再进行求余运算。

在进行强制类型转换时,得到一个所需类型的中间值,原来变量的类型及该变量所存储的值并未发生变化。【实例4-8】强制类型转换示例。#includevoid main(){float x;int a;x=3.5;a=(int)x; /*运用强制类型转换将浮点型数据转换成整型数据*/printf("x=%f,a=%d\n",x,a);}

程序运行的结果为:x=3.500000,a=3

自动转换一般不会使数据受到损失,而强制转换就有可能使数据受损或结果难以理解,这是由于高级别的类型转换为低级别的类型时无法完整存储造成的。我们可以像下面这样理解数据类型之间的转换。(1)实数型之间的转换

将单精度转换成双精度,数据处理的方法是数据所占的字节由4字节变成8字节;有效数字由7位变成16位;数字大小没有变化。

将双精度转换成单精度,数据处理方法是数据所占的字节由8字节变成4字节;截取前面7位有效数字,数字可能不准确,注意溢出。例如:float f;double d=123.456789e100;f=d;printf("%f",f);

这段代码写在程序里面,在链接编译时并不会出现错误,但会给出将double型转换成float型可能会丢失数据的警告提示。(2)整型与实数型之间的转换

将整型向实数型数据转换时,要补充小数位数和精度,如:整型数2转换成单精度浮点型为2.000000。

将整型向实数型转换时,将截断小数位,只保留整数部分,而不是四舍五入,同时要注意数据的溢出。如:2.8转换成整型数为2。(3)字符型与实数型之间的转换

字符型转换成实数型时,数据由字符型的存储方式转变成浮点数的存储方式,数字大小不变,补足有效位数。实数型转换成字符型时,以字符型的方式存储,舍弃小数部分,注意数字的溢出。(4)整型之间的转换

较长整型向较短整型转换时,要截断高位,只保留低位数据。

较短整型向较长整型转换时,将较短整型数的16位送到较长整型的低16位中,如果较短整型为正值(符号位为0),则较长整型变量的高16位补0;如果较短整型变量为负值(符号位为1),则较长整型变量的高16位补1,以保持数值不变。如果是无符号数,低位直接赋值,高位补0即可。(5)有符号数向无符号数转换时,原来的符号位不再作为符号,而变为数据的一部分;无符号数向有符号数转换时,最高位被当做符号位。

4.6 本章小结

C语言中的数据类型分为四大类:基本数据类型、构造类型、指针类型和空类型。不同的数据类型在计算中所占的空间不同。字符型(char)占一个字节,整型数据中普通整型(int)占4个字节、短整型(short)占2个字节和长整型(long)占4个字节。单精度浮点型(float)占4个字节,双精度浮点型(double)占8个字节。不同的数据类型之间相互转换时,根据需要进行自动类型转换和强制类型转换。

4.7 习题

1.C语言中的数据类型分为哪几类?

2.简述整型常量的概念,并举例说明什么是整型常量。

3.实数型常量的表示方法有哪些?

4.数值在进行类型转换时,应该注意哪些问题?

5.编写一个程序,计算整数1~30的和。

第5章 运算符及其表达式

运算符是指用来对运算对象进行各种运算的操作符号。表达式是指由多个运算对象和运算符组合在一起的合法算式。其中运算对象包括常数、常量、变量和函数。本章内容如下: 算术运算符及算术表达式; 赋值运算符及赋值表达式; 关系运算符及关系表达式; 逻辑运算符及逻辑表达式; 条件运算符及条件表达式; 逗号运算符及逗号表达式; 位运算符。

5.1 算术运算符及算术表达式

算术运算符与我们数学中的运算符相似,但也有不同之处。5.1.1 算术运算符

算术运算符包括基本算术运算符和自增、自减运算符。基本算术运算符对数值型也包括字符型数据进行加、减、剩、除四则运算。自增自减运算符对字符型、整型等变量进行加1、减1运算。算术运算符的运算的表达方式、运算功能如表5-1所示。表5-1 算术运算符

下面是算术运算符的具体运用原则。 +(正)、-(负)运算符是属于同一级别的单目运算符,结合方

向是自右向左。 +(加)、-(减)运算符是属于同一级别的双目运算符,结合方

向是自左向右。例如a+b-c+d,这个式子在运算顺序是先求a与b

的和,然后减去c,得到差与d相加。在C语言中,加、减运算符

与数学中的运算符含义完全相同。 *,/,%是同一级别的双目运算符,结合方向是自左向右。即它

们在运算过程中同时出现时,按照它们出现的顺序进行运算。这

三种运算的优先级别高于+(加)、-(减)运算符。例如:a

+b*c,运算顺序是先计算b与c的乘积,然后再与a求和,即a

+(b*c)。

需要注意以下几点: /(除法运算符)的除数不能为0,即不能用一个数去除以0。 *(乘法运算符)在式子中不能省略,也不能写成是代数式子中

的乘号“×”或“·”。这一点要特别注意。例如:求长方体的体

积公式为abc,在编程时要写成a*b*c。 两个整型数相除,得到整型结果。如果两个实数相除或其中有一

个是实数,那么得到的结果为实数型。例如:5/3=1,2/4=0,5/-3=1,5./3=1.666667,5.0/3.0=1.666667 %求余运算符(或称求模运算),只适合于整型数据和字符型数

据。求余运算的结果符号与被除数相同,其值等于两数相除后的

余数。5%3 /* 值为2 */3%5 /* 值为3 */10%2 /* 值为0 */-2%-3 /* 值为-2 */-7%-3 /* 值为-1 */ ++、——(自增、自减运算符)属于同一级别的单目运算符,

结合方向是自右向左。自增、自减运算符只能与变量结合使用,

放在变量的前面或后面。有以下4种形式。 ++a:a的值先增加1后,再参与其他运算。 a++:a的值先参与其他运算,再使a的值增加1。 ——a:a的值先减小1后,再参与其他运算。 a——:a的值先参与其他运算,再使a的值减小1。

例如:m=3;m1=m++;。

运算的过程是先将m的值赋给m1,然后m的值再加1,上面两个语句等价于:m=3;m1=m;m++;【实例5-1】“++”和“——”运算符的使用。#include void main( ){int k1,k2,a,b; /*定义四个整型变量*/k1=3; /*对k1赋值为3*/k2=3; /*对k1赋值为3*/a=++k1; /* k1的先增加1后,再赋值给变量a*/b=k2——; /*k2的先赋值给变量b,然后k2的值再增加1*/printf("a=%d,k1=%d\n",a,k1); /*输出a、k1的值*/printf("b=%d,k2=%d\n",b,k2); /*输出b、k2的值*/}

程序运行的结果为:a=4,k1=4b=3,k2=2

对于自增、自减运算符,做以下几点说明: 自增或自减函数只能用于变量,不能用于常量或表达式。例如,

(a+b)++这样的表示方法是错误的。 在一个表达式中对一个变量自增或自减多次,可能造成困惑。

a=3;k=(++a)+(++a);这种程序很容易出错,在编程的过程中要避

免使用这样的程序,而且也没有必要使用如此难懂的程序,完全

可以使用另一种方法来表示,增加程序的可读性。 ++、——运算符的结合方向是自右向左,即当优先级别相同的

运算符在一起时,从右向左算,如:-i++等价于-(i++)。 ++、——运算符的优先级大于乘、除、求余的优先级。 ++、——运算符运算的操作对象只能为整型变量、字符型变量

和指针变量,而不能是其他类型的变量。 ++、——运算符运算常用于循环变量中,是循环变量自动加1或

减1;也可用于指针变量,是指针指向前一个或后一个地址。++、

——运算符在C语言的程序设计中是很有用的,在以后的章节中

我们会经常用到,为程序设计增加了便利。5.1.2 算术表达式

用算术运算符将运算对象即运算量或操作数连接起来,构成符合C语言语法规则的式子,称为算术表达式。算术表达式中,运算对象包括常量、变量和函数。算术表达式求值规律与数学中的规律类似。例如,x+y*a/x-5%3,3.5+56%10+3.14,a++*1/3。

这些都是正确的算术表达式。关于算术表达式有以下几点说明: 算术表达式的求值顺序按算术运算的优先级别高低次序进行,先

执行优先级别高的,再执行优先级别低的。例如,先算乘除后算

加减,有括号先算括号里面的。以表达式8%3+9/2为例,%、/

运算符的优先级高于+运算符的优先级,因此在运算的过程中先

算求余和除法,8%3=2,9/2=4,然后再求和2+4=6,因此最后

的结果为6。 在算术表达式中,运算对象有常量,也有变量。当为变量时,可

能出现数据类型不同的情形,这时需要对其进行数据类型的转

换,有的是系统自动完成的,有的需要运用强制类型转换符进行。

例如:'a'+5*2

在算术表达式求值中,先求5*2的积,再求与‘a’的和。因为5*2的结果为整型,而‘a’为字符型,因此在运算过程中,系统自动将字符型转换为整型即取‘a’的ASCII码值97,然后再求和,最后的结果为107。(double)(8%3)

将8%3的值转换成双精度类型。【实例5-2】算术表达式的应用举例。#include void main( ){int a=1,b=4,c=2;float x=10.5,y=4.2,z;z=(a+b)/c+(int)y%c*1.2+x;printf("%f\n",z);}

程序运行的结果是:12.500000。

5.2 赋值运算符及赋值表达式

在前面介绍的变量初始化中,对变量的初始化赋值时,需要用到赋值运算符。C语言的赋值运算符包括简单赋值运算符和复合赋值运算符,本节主要讲解简单赋值运算符,复合赋值运算符将在位运算符一节中进行详细的说明。5.2.1 赋值运算符

赋值运算符与代数里面的等号相同,即“=”。赋值运算符的作用是把运算符右边的表达式的值赋给其左边的变量,结合性是从右向左。例如:a=5;

该语句的作用是把5赋给变量a,即把5存入变量a所对应的存储单元里面。

赋值运算符号“=”之前加上其他运算符,就可以构成复合赋值运算符,如+=,-=、/=等。C语言中常用的赋值运算符如表5-2所示。表5-2 赋值运算符

根据表5-2,对赋值运算符我们有以下几点认识:(1)赋值运算符“=”左边必须是变量,右边可以常量、变量,也可以是函数调用或表达式。例如下面的赋值方式都是合法的。ch='a';b=c;s=a*b/c-12.34d=a*b*sin(A)/2.0;

在赋值的过程中,赋值运算符的右边常量、变量、函数或表达式的值不是左边变量的类型时,需要进行自动类型转换或强制类型转换。例如:int a; a='b';

定义一个整型变量a,将字符'b'赋值变量a。这是两个不同的数据类型,在赋值过程中,系统自动的将字符'b'转换成整型数据,即取字符'b'的ASCII码值98,赋值给变量a。

再如:double d;int a;a=(int)d%5;

求余的两个操作数必须为整型数,而d为double型数据,因此要将其转换成整型,从级别比较高的数据类型向级别比较低的数据类型转换时需要进行强制类型转换。(2)赋值与运算符“=”与数学中的等号“=”看起来相同,但是它们的含义、作用完全不同。例如:a=a+2;

在数学中,这个式子是不成立的,因为式子的左边和右边是不相等的。而在C语言中,这个式子是完全正确的,它表示的含义是将变量a当前的值加2,再把结果赋给变量a。(3)复合赋值运算符是由其他运算符与基本的赋值运算符组合而成的。复合赋值运算符的左边必须是一个变量,右边可以是常量、变量、函数或表达式,当右边为表达式时,要将右边的所有部分都看成是一个整体,不能把它们开分。

例如:a+=3;相当于:a=a+3;,b*=a;相当于b=b*a;。h/=x+y;不能理解为:h=h/x+y,应该理解为h=h/(x+y),它表示的是h除以x加y的和,然后将商赋给h。再如:a+=b+5;相当于a=a+(b+5);,a*=b-c;相当于a=a*(b-c);,a<<=b+c;相当于a=a<<(b+c);。采用这种复合赋值运算符,一方面简化了程序,另一方面提高了编译的效率。5.2.2 赋值表达式

由赋值运算符将一个变量和一个表达式连接起来的式子称为赋值表达式。一般的书写形式如下:变量 赋值运算符 表达式

例如:a=10b=c+da/=d+2

在赋值表达式后添加一个分号,就成为赋值语句。

例如:a=10;b=c+d;a/=d+2;

对于赋值表达式,需要说明以下几点:(1)赋值运算符的左边必须为变量,而赋值表达式的左边可以是变量,也可以是赋值表达式。当赋值表达式的左边是赋值表达式的时候,应该带上括号。例如:(a=3*4)=4*6

这种表示是正确的。a=3*4=4*6

这种表示是错误的,因为3*4是常量,不能作为左值。(2)赋值表达式右边的表达式可以是一个算术表达式、关系表达式、逻辑表达式等,也可以是一个赋值表达式。例如:c2=c1=5

因为赋值运算符的结合方向是从右向左,因此上式相当于c2=(c1=5),c1=5是一个赋值表达式,表示将5赋值给c1,c2=(c1=5)表示将c1=5赋值表达式的值5赋给c2,c2的值为5,整个表达式的值也就为5。再如:a=(b=1)+4

这个赋值表达式表示a的值为赋值表达式b=1的值加4的和。因此整个表达式的值为5,其中的两个变量的值分别为a=5,b=1。z=(x=2)*(y=3)

这个赋值表达式表示z的值为赋值表达式x=2的值与赋值表达式y=3的值相乘的积。该式子相当于z=2*3,因此整个表达式的值为6,其中的三个变量的值分别为x=2,y=3,z=6。(3)赋值表达式里面可以包含复合赋值运算符。例如:c2=c1+=1

这个赋值表达式相当于c2=(c1+=1),而c1+=1等价于c1=c1+1,因此该式子相当于c2=(c1=c1+1)。假设c1的值为6,赋值运算方向是从右向左,首先计算c1=c1+1表达式的值,结果c1的值为7,再运算表达式c2=(c1=7),因此整个表达式的值为7,最后c1=7,c2=7。【实例5-3】设整型变量a=4,执行a+=a-=a*a后,求a的值。

例题分析:

a+=a-=a*a是由两个复合赋值运算符组成的表达式,由于赋值运算符的结合性是从右向左,因此该表达式相当于a+=(a-=a*a)。

第一步:a=4,求a-=a*a,a-=a*a等价于a=a-(a*a),a=4,于是a=4-4*4=-12。第一步运算结束后a的值由原来的4变成了现在的-12。

第二步:a=-12,求a+=a-=a*a即a+=a,a+=a等价于a=a+a,此时a=-12,于是a=(-12)+(-12)=-24。第二步运算结束后a的值由-12变成了-24,因此执行a+=a-=a*a后,a的值为-24。(4)在C语言中,赋值操作不仅出现在赋值语句中,而且可以以表达式形式出现在其他语句中。例如:printf("%d",a=b=3);

程序执行时,首先计算出表达式a=b=3的值,这个输出语句最后输出的是a的值为3。

5.3 关系运算符及关系表达式

C语言中关系运算常用于选择结构、循环结构的条件判断。由关系运算符连接的式子称为关系表达式,用于条件的判断。5.3.1 关系运算符

关系运算符是用来比较两个运算量大小的运算符,实际上就是一种“比较运算”,运算的结果只能是“1”或“0”。当两者的比较关系成立的时候,结果为“1”;当两者的比较关系不成立的时候,结果为“0”,因此关系运算符的结果类型为整型。C语言中常用的关系运算符如表5-3所示。表5-3 关系运算符

根据表5-3,对关系运算符进行以下几点说明:(1)关系运算符的优先级别比算术运算符的级别低,但比赋值运算符的级别高。而所有的关系运算符的优先级别也不相同,如表5-3所示,前4种运算符(<、<=、>、>=)的优先级别相同,后面两种(= =、!=)优先级别相同。前面四种的优先级别高于后面两种的优先级别。因此关系运算符的运算优先级别如图5-1所示。图5-1 关系运算符的优先级

例如:a=2*2<8

该式子的运算顺序为a=((2*2)<8),用括号表示其运算优先级,首先算术运算符优先,计算2*2=4;关系运算符次之,即4<8,关系成立,结果为1,最后是赋值运算符,将关系运算的结果赋值给变量a,即a=1。(2)关系运算符用于比较的两个运算量的类型为整型、字符型等,也可以连接两个表达式,比较的结果是一个逻辑量,即“真”或“假”,在C语言中没有逻辑型数值,分别用整数1和0表示。例如:5>=2

该表达式成立,结果为真,用“1”表示。5-2>=7+1

由于算术表达式的优先级高于关系运算符,因此要先计算关系表达式两侧的算术表达式,然后再进行比较,3>=8显然不成立,结果为假,用“0”表示。(3)关系运算符的结合方向是从左向右,因此当一个表达式中出现优先级相等的关系运算符时,从左向右开始运算。例如:b= =a>c

从图5-1可以看出“>”优先级高于“= =”的优先级,因此表达式b= =a>c等价于b= =(a>c)。如果a=5,b=1,c=3,即1= =5>3,5>3为成立,关系运算的结果为1,1= =1表达式成立,最后表达式b= =a>c的结果为1。

在所有的运算符当中,括号“(  )”的优先级别最高,为了明确运算的顺序,增加程序的可读性,最好将优先运算的表达式用括号括起来。例如:a=b>=c

从图5-1我们可以看出关系运算符的优先级高于赋值运算符,但是为了避免运算混淆,将a=b>=c写成a=(b>=c),表示b>=c的结果赋值给变量c,将用括号使运算更加清晰明了。(4)在关系运算符用“= =”表示等于,用“!=”表示不等于,这与数学中的表示方法完全不同,因此在编程中要特别注意,以免写错关系运算符而导致错误的结果。例如:a==b

表示判断a与b是否相等,结果是一个整型数,“1”或者“0”。而a=b

表示将b赋值给a。5.3.2 关系表达式

用关系运算符将两个表达式连接起来构成的式子称为关系表达式。一般的书写形式如下:表达式 关系运算符 表达式

其中,表达式可以是算术表达式、关系表达式、逻辑表达式、赋值表达式等,关系表达式的数据类型为整型,结果值只能是“1”或“0”。

例如,假设定义变量:char c='A';int a=2;float f=3.56;'A'>(c='a')

该关系表达式中的表达式为赋值表达式,将字符'a'赋值给变量c,即'A'>'a','A'的ASCII值为65,'a'的ASCII值为97,即65>97,关系不成立,关系运算的结果为0。cf

该关系表达式中的表达式为关系表达式,该关系表达式等价于(cf),'A'的ASCII值为65,因此关系表达式cf转换成(65<2)!=(2>3.56),表达式65<2的结果为0;表达式2>3.56,整型数与实数型数进行比较,要进行类型转换即整型数自动转换成实数型数,即2.0>3.56,结果为0。(65<2)!=(2>3.56)转化成0!=0,整个关系表达式的值为0。【实例5-4】关系表达式的应用举例。#include void main( ){int a,b,c; /*定义变量*/a=1+5>2*4; /*先进行判断,然后确定a的值*/b='A'<'B'; /*先进行判断,然后确定b的值*/c=a==b;printf("a=%d,b=%d,c=%d\n",a,b,c); /*输出a、b、c的值*/}

程序运行的结果为:a=0,b=1,c=0

关系表达式实现两个运算量之间的比较,主要用于程序设计中选择结构的条件判断,例如:if(a>b) c=a;

这段程序代码表示:如果关系表达式a>b成立,那么把变量a的值赋给变量c。

通过关系表达式,程序通过判断,选择执行某段程序代码还是跳过,很容易实现跳转,使程序设计更加灵活自如。

5.4 逻辑运算符及逻辑表达式

逻辑运算符与关系运算符经常放在一起使用。关系运算是指值与值之间的关系,逻辑运算是指将真值和假值连接在一起的方式。由于关系运算符产生了真或假的结果,所以关系运算表达式中常常使用逻辑运算符。5.4.1 逻辑运算符

逻辑运算符是对两个含有关系运算符的表达式或逻辑值进行运算的符号,运算的结果为逻辑值。

逻辑运算符的表示形式、结合性等如表5-4所示。表5-4 逻辑运算符

根据表5-4,对逻辑运算符做以下几点说明:(1)“&&”和“||”是双目运算,需要两个操作数,如a&&b,a||b。而“!”是单目运算符,只需要要一个操作数,如!a。(2)三个逻辑运算符的优先顺序如图5-2所示。图5-2 三个逻辑运算符的优先顺序

由此可知,逻辑非的优先级高于逻辑与的优先级,而逻辑与的优先级又高于逻辑或的优先级。(3)目前为止已经学习了算术运算符、赋值运算符、关系运算符和逻辑运算符,这些运算符之间的运算优先顺序是逻辑非(!)运算符优先级最高,算术运算符优先级高于关系运算符,关系运算符又高于逻辑与(&&)和逻辑或(||),而赋值运算符优先级最低,如图5-3所示。图5-3 逻辑运算符的优先级

例如:10

由于关系运算符的优先级高于逻辑与(&&),10

用逻辑运算符进行逻辑运算时,结果只有真或假两种情况,在C语言中,用“非0”表示真,“0”表示假。因此逻辑运算有一定的运算规则,如表5-5所示。表5-5 逻辑运算规则

从表5-5可以看出:参加逻辑运算的对象,用“0”表示假,用“非0”表示真,由此看来,任何数值型的数据都可以看成逻辑值,所以逻辑运算符的运算对象可以是关系运算结果,还可以是整型、实数型及字符型等数据。例如:!(a>b)运算的对象为关系运算结果,4&&0||2运算的对象为整型数值。

逻辑运算的结果是一个逻辑值,即真或假。在C语言中,在判断一个逻辑运算对象是否为真时,以“非0”代表真,以“0”代表假,而在给出逻辑运算的结果时,以整型数值“1”代表真,以“0”代表假。例如:'B'&&10

'B'的ASCII值为66,其值为非0,即为真;10为非0,也为真,对照表5-5所示的逻辑符的运算规则表,两个运算对象都为真时,它们的逻辑与为真,因此'B'&&10的运算结果为1,表示为真。

逻辑运算的运算规则可简单归纳为: 逻辑与同真为真,即在进行逻辑与运算时,只有所有的运算对象

都为真时,运算结果才为真,当有一个运算对象为假时,逻辑与

的运算结果就为假。例如:a&&b&&c

当a、b、c都为非0值时,该逻辑表达式为真,即逻辑结果为1,至少有一个为0,逻辑表达式就为假,值为0。 逻辑或同假为假,即在进行逻辑或运算时,只有所有运算对象都

为假时,运算的结果才为假,当有一个运算对象为真时,逻辑或

的运算结果就为真。例如:a || b || c

当a、b、c都为0值时,该逻辑表达式为假,即逻辑结果为0,至少有一个为非0,逻辑表达式就为真,值为1。 逻辑非遇假变真,遇真变假,即运算对象为真时,逻辑非的结果

为假,运算对象为假时,逻辑非的结果为真。例如:!a

当a为非0值时,!a值为0;当a为0时,!a的值为1。5.4.3 逻辑表达式

由逻辑运算符连接起来构成的表达式称为逻辑表达式。逻辑运算的对象通常是关系表达式、逻辑表达式,也可以是算术表达式、赋值表达式等其他的表达式。例如:a>10 && a<15 /*逻辑表达式的运算对象是关系表达式*/!(a<=10) &&!(a>=15) /*逻辑表达式的运算对象是逻辑表达式*/(m=a>b)&&(n=c>d) /*逻辑运算符的运算对象是赋值表达式*/t=++x||++y&&++z /*逻辑运算符的运算对象是算术表达式*/

与关系表达式一样,逻辑表达式的值也是一个逻辑量,逻辑量为真时,值为1,逻辑量为假时,值为0。例如:假设a=11,对于逻辑表达式!(a<=10) &&!(a>=15),逻辑非(!)的优先级高于逻辑与(&&)的优先级,逻辑与(&&)的结合方向是自左向右。

首先求!(a<=10),将a=11代入即!(11<=10),括号的优先级最高,11<=10该关系不成立,值为0,!0的逻辑值为1,因此表达式!(11<=10)的值为1。然后求表达式!(a>=15),同理,a>=15等价于11>=15关系不成立,值为0,!0的逻辑值为1,因此表达式!(a>=15)的值为1。!(a<=10) &&!(a>=15)等价于1&&1,根据逻辑运算规则,逻辑与(&&)所有运算对象为真时,整个运算表达式为真,结果为1。

由于逻辑运算表达式的运算对象可以是各种表达式,逻辑运算有时比较复杂,需要注意以下几点:(1)在一个逻辑表达式中可以包含多个逻辑运算和其他各种运算符,首先注意哪些是数值运算,哪些是关系运算,哪些是逻辑运算,搞清各个运算符之间的关系,然后按它们的优先级进行运算。【实例5-5】设a=3,b=4,c=5,求逻辑表达式!(a+b)*c-1&&b+c%2的值

例题分析:

第一步:由图5-3可知,逻辑与(&&)的优先级低于逻辑非(!)、关系运算符、算术运算符,因此逻辑表达式!(a+b)*c-1&&b+c%2等价于(!(a+b)*c-1)&&(b+c%2),因为逻辑与(&&)结合性是自左向右,所以运算从左边开始。

第二步:进行!(a+b)*c-1的运算。根据优先级别,逻辑非(!)优先于算术运算符,首先进行逻辑非(!)的运算,!(a+b)代入值后为!(3+4)=!7,逻辑运算的结果为0,!(a+b)*c-1等价于0*c-1,乘法的优先级高于减法,因此运算的结果为-1。

第三步:进行b+c%2的运算。由于求余运算符的优先级高于加法,因此首先进行求余运算,即4+5%2=4+1=5。

第四步:进行逻辑与运算。逻辑表达式!(a+b)*c-1&&b+c%2最后等价于-1&&5,-1和5都为非0的整型数,根据逻辑运算规则,两个非0值相与结果为真,即为1,因此该逻辑表达式最后的结果为1。(2)逻辑表达式在进行求值的过程中,不一定必须将表达式求值到底,这是逻辑运算的特殊性所在,称为短路运算。例如,a&&b&&c。

根据逻辑运算的运算规则,“逻辑与同真为真”,只有当a、b、c都为真的时候,表达式才为真,只要有一个为假,表达式就为假。因此,首先判断a,当a为假时,这个表达式就为假,后面的b、c无须进行判断。只有当a为真时,才判断b,当b为假时,这个表达式就为假,无须判断c。当a&&b为真时才判断c,例如:a || b || c。

同理,“逻辑或同假为假”,只要a为真,就不必再继续判断b和c,结果一定为真。只有当a为假时,才对b进行判断,依此类推。

逻辑运算表达式的运用场合与关系表达式完全相同,也用于流程控制语句的条件描述。逻辑运算符连接关系表达式用于复合条件的描述。

ch是字符变量,判断ch是英文字母的逻辑表达式为:ch>=’a’ && ch<=’z’ || ch>=’A’&&ch<=’Z’

在ASCII表中,英文大小写字母分别对应一个数值,变量ch为英文字母,或为小写或为大写,若为小写,取值应在'a'~'z'之间,即ch>='a'和ch<='z',两者都要满足;若为大写,取值应在'A'~'Z'之间,即ch>='A'和ch<='Z',两者也要同时满足,因此用逻辑与(&&)连接。ch为大写还是为小写,这两个条件只要有一个满足就可以了,因此用逻辑或(||)连接。【实例5-6】输入若干个字符,分别统计数字字符的个数、英文字母的个数,当输入换行符时输出统计结果,运行结束。#include void main(){char ch;int s1=0; /*定义整型s1,记录数字的个数*/int s2=0; /*定义整型s2,记录数字的个数*/while(( ch=getchar( ) )!='\n'){if(ch>='0'&&ch<='9') /*判断输入的字符是否为数字*/{s1++;}if(ch>='a'&&ch<='z' || ch>='A'&&ch<='Z' ) /*判断输入的字符是否为字母 */{s2++;}}printf("%d\t%d\n",s1,s2);}

输入一段字符:Olympic Games 2008↙

程序运行的结果为:4 12

变量a、b中必有且只有一个为0的逻辑表达式为:a==0&&b!=0||a!=0&&b==0

根据题意,变量a、b中必有且只有一个为0,即当a为0时,b不能为0,这两者必须同时满足;当a不为0时,b必须为0,这两者也是需要同时满足的,因此都用逻辑(&&)与连接。而这两情况只能满足其中的一个,用逻辑或(||)连接。

m是值为两位数的整型变量,判断其个位数是奇数而十位数是偶数的逻辑表达式为:m/10%2= =0&&m%2= =1

个位数是奇数和十位数是偶数这两个条件是要同时满足的,因此要用逻辑与(&&)。判断一个数是偶数还是奇数,让它对2求余,如果余数为0,说明能被2除尽为偶数,如果余数为1,说明不能被2除尽为奇数。判断个位数是否为奇数,即判断这个数是否为奇数,用这个数对2进行求余,判断余数是否与1相等,即m%2= =1;判断十位数是偶数,用m/10求出其十位上的数字,然后对2进行求余,判断余数是否为0,即m/10%2= =0。【实例5-7】输入一个正整数,判断其是否个位数是奇数而十位数是偶数,若是,输出“YES”,若不是,输出“NO”。#include void main( ){int k; /*定义一个整型变量*/scanf ("%d", &k); /*输入一个整型数据*/if (k/10%2==0&&k%2==1) /*判断其是否个位数是奇数而十位数是偶数*/{printf("YES\n");}else{printf ("NO\n");}}

输入一个整型数:25↙

程序运行的结果为:YES

5.5 条件运算符及条件表达式

条件运算符是C语言中唯一的三目运算符,它根据一个表达式的结果等于true还是false,执行两个表达式中的一个。由于涉及三个操作数——一个用于判断的表达式和另外两个表达式,因此这个运算符也称为三元运算符。5.5.1 条件运算符

条件运算符是由符号“?”和“:”组合而成的。条件运算符有三个运算对象,三个运算对象都表达式。第一个运算对象可以是任何类型的表达式,如算术表达式、关系表达式、赋值表达式和逻辑表达式等,后面两个表达式是类型相同的任何表达式。条件运算符的表达方式、结合方向、功能如表5-6所示。表5-6 条件运算符

从表5-6可以得出:(1)在程序设计中,条件运算符可以用于程序的判断和选择。可以用条件运算符非常简单地计算出两个变量中比较大或比较小的那个值。例如:a>=b?a:b

上面的式子表示求两个变量中数值比较大的那个变量。如果a>=b为真,表示a是两个数中比较大的,结果就是a;如果a>=b为假,表示b是两个数中比较大的,结果就是b。(2)条件运算符的结合方向是自右向左。当一个式子中出现多个条件运算符时,应该将位于最右边的问号与离它最近的冒号配对,并按这一原则正确区分各条件运算符的运算对象。

例如:w

该式子中含有两个条件运算符,根据上述的运算规则,我们很容易得出x

条件运算符的优先级仅仅高于赋值运算符和逗号运算符,低于所有其他运算符,即条件运算符优先于赋值运算符,低于逻辑运算符、关系运算符和算术运算符,如图5-4所示。图5-4 条件运算符的优先级【实例5-8】条件运算符的使用。char c='A';int a=2,b=3;

表达式一:c>='A'&&c<='Z'?a:b

由图5-4条件运算符的优先级可知,逻辑运算符的优先级高于条件运算符,因此上式等价于(c>='A'&&c<='Z')?a:b。关系运算符的优先级又高于逻辑运算符,因此(c>='A')&&(c<='Z'),由于c='A',所以关系表达式c>='A'为真,c<='Z'也为真,根据逻辑与(&&)同真为真的运算规则,逻辑表达式(c>='A')&&(c<='Z')结果为真,因此条件运算表达式c>='A'&&c<='Z'?a:b的运算结果为a,即值为2。

表达式二:b=3+a>5 ? 100 : 200

根据条件运算符的优先级可知:条件运算符优先于赋值运算符,因此上式等价于b=(3+a>5 ? 100 : 200),又因为条件运算符优先级低于关系运算符和算术运算符,所以表达式3+a>5 ? 100 : 200又等价于(3+a>5) ? 100 : 200。3+a>5等价于3+2>5,即5>5,逻辑值为假,所以条件表达式3+a>5 ? 100 : 200的运算符结果为200,然后将条件表达式的值,赋值给变量b,因此b=200。5.5.2 条件表达式

由条件运算符连接而构成的表达式称为条件表达式。一般的表达形式为:表达式1 ? 表达式2 : 表达式3

关于条件表达式做以下几点说明:(1)条件表达式中含有三个操作对象,它们都是表达式,可以是各种类型的表达式。通常情况下,表达式1是关系表达式或逻辑表达式,用于描述条件表达式中的条件,根据条件的真假来判断是进行表达式2的运算还是进行表达式3的运算。表达式2和表达式3可以是常量、变量或表达式如算术表达式、关系表达式、赋值表达式和逻辑表达式等。例如:y>z? x+2 : x-2(a= =b)? 'A': 'B'ch=(ch>='A'&&ch<='Z')?(ch+32):ch

这些都是正确的条件表达式。(2)条件表达式的求解过程:

第一步:求解表达式1的值。

第二步:如果表达式1的值为真即为非0,求解“表达式2”的值作为整个条件表达式的值。

第三步:如果表达式1的值为假即等于0,求解“表达式3”的值作为整个条件表达式的值。【实例5-9】设ch是char型变量,其值为'A',求表达式ch=(ch>='A'&& ch<='Z')? (ch+32) : ch的值。

例题分析:

① 根据运算符的优先级可知,条件运算符的优先级高于赋值运算符,因此首先计算条件运算符表达式(ch>='A'&& ch<='Z')? (ch+32):ch。

② 根据条件表达式的运算顺序,首先要求解表达式ch>='A'&& ch<='Z',由于ch='A',因此逻辑表达式ch>='A'&& ch<='Z'为真,所示求表达式ch+32。

③ 字符型变量ch=的'A',对应的ASCII码值为65,ch+32=65+32=97,因此条件表达式(ch>='A'&& ch<='Z')? (ch+32):ch最终的结果为97。

④ 将条件运算符的结果赋值结字符型变量ch。因为97在ASCII码中对应的字符为'b',所示ch='b'。

由此可知,表达式ch=(ch>='A'&& ch<='Z')? (ch+32) : ch实现的功能是:

首先判断字符型变量是不是大写字母,如果是大写字母,将大写字母转换成小写字母;如果不是大写字母则直接将该字符赋值给ch变量。(3)条件表达式允许嵌套使用,即允许条件表达式中的表达式2和表达式3又是一个条件表达式。【实例5-10】若a=13、b=25、c=-17,求条件表达式((y=(a

例题分析:

① 根据括号的关系可以判断条件表达式((y=(a

② 在表达式(y=(a

已知a=13、b=25、c=-17,a

③ 由于表达式(y=(a

例如:max=(a>b)?a:b;

用if语句表示为:if(a>b){max=a;}else{max=b;}

条件表达式用流程图表示,如图5-5所示。图5-5 条件表达式的流程图

但并不是所有的条件表达式与if语句都能替换,只有当if语句中内嵌的语句为赋值语句,并且两个分支都赋给同一个变量时,才能用条件运算符代替。

例如:if(a>b){printf("%d", a) ;}else{printf("%d", b) ;}

就不能写成像下面这样的形式:a>b?printf("%d",a): printf("%d",b);

因为条件表达式的结果是一个值,要将这个值赋给一个变量或以一个值的形式输出,而上面的形式表示的是,如果a>b为真,就将a以整型的形式输出,否则将b以整型的形式输出,而在实际编程过程中无法将其值输出。

但可以用下面的语句:printf("%d", a>b ? a : b) ;

这是一个printf输出语句,输出列表是一个条件表达式。这个语句的作用是:如果a>b为真,就将a以整型的形式输出,否则将b以整型的形式输出。(5)条件表达式中,表达式1的类型可以与表达式2、表达式3的类型不同,表达式2与表达式3的类型也可以不同,此时表达式值的类型为两者中较高者的类型。例如:f=x>y ? 2:3.14

该表达式的作用是,如果x>y为真,条件表达式x>y ? 2:3.14的结果为2;如果x>y为假,条件表达式x>y ? 2:3.14的结果为3.14。因此条件表达式中表达式2是一个整型常量,而表达式3是一个浮点型常量,两个操作对象是不同的数据类型,该条件表达式的值要取两者中较高的类型,即为浮点型。

因此,如果x>y为真,条件表达式x>y ? 2:3.14的结果为2.0。【实例5-11】条件表达式应用举例。#include void main( ){int a=3;int b=4;int c;c=a>b?++a:++b;printf("a=%d,b=%d,c=%d\n",a,b,c);}

程序运行的结果:a=3,b=5,c=5

程序运行到第7行时,条件运算符的优先级高于赋值运算符,首先对条件表达式进行运算,a=3,b=4,所示a5.6 逗号运算符及逗号表达式

C语言编程中,经常会用到逗号,比如在定义多个同一种类型的变量的时候,各个变量之间用逗号隔开;标准输入/输出函数中,输入/输出列表的各个对象之间也是用逗号隔开的。逗号在C语言中可以作为一种运算符使用,称为逗号运算符。5.6.1 逗号运算符

逗号运算符(,)是C语言中提供的特殊运算符。逗号运算符的具体说明如表5-7所示。表5-7 逗号运算符

根据表5-7对逗号运算符做以下几点说明:(1)逗号运算符是双目运算符,运算的对象可以是任何类型的表达式,运算的结果值是最后一个表达式的值。例如:335,668c=b*5,c=8*5,c=40y=(x=a+b),(b+c)

这些都是逗号表达式。(2)逗号运算符是所有运算符中优先级最低的,如图5-6所示。图5-6 逗号运算符的优先级(3)逗号运算符的结合方向是自左向右。逗号运算符将表达式连接起来,运算的时候按连接的顺序依次进行运算,所以又称为顺序求值运算符。(4)并不是任何地方出现的逗号都作为逗号运算符,有的时候逗号用于各个对象之间的间隔。例如:printf("%d,%d,%d",a,b,c);

输出函数中输出列表“a,b,c”中的逗号并不是逗号运算符。a,b,c是printf函数的3个参数,参数间用逗号间隔。int a,b,c;

这个语句定义了三个变量,三个变量之间的逗号不是逗号运算符,逗号在这里起间隔作用,表示的是分别定义了三个整型变量。这个语句等价于:int a;int b;int c;5.6.2 逗号表达式

用逗号运算符将表达式连接起来构成的表达式就称为逗号表达式。逗号表达式的语法格式为:表达式1,表达式2

对于逗号表达式做以下几点说明:(1)逗号表达式的求解过程是:先求解表达式1,再求解表达式2。整个逗号表达式的值是表达式2的值。【实例5-12】若已定义x和y为double类型,则求表达式:x=1,y=x+3/2的值。

例题分析:

该表达式是一个逗号表达式,逗号运算符的结合方向是自左向右,所以先运算x=1,变量x为double类型,进行自动类型转换,结果变量x中的值为1.0。然后运算y=x+3/2,y=1.0+1,其结果是变量y中的值为2.0,逗号表达式y=x+3/2的值即等于变量y的值为2.0。最后,整个逗号表达式的值应该等于最后一个表达式的值2.0。(2)逗号表达式在求解的过程中要注意各个运算符之间的优先级,逗号运算符的优先级最低。例如:a=3*5,a*4

对此表达式的求解,读者可能会有两种不同的理解:一种认为“3*5,a*4”是一个逗号表达式,先求出此逗号表达式的值,然后再将逗号表达式的值赋给变量a。如果a的原值为3,则逗号表达式的值为12,将12赋给a,因此最后a的值为12。另一种认为:“a=3*5”是一个赋值表达式”,“a*4”是另一个表达式,二者用逗号相连,构成一个逗号表达式。

赋值运算符的优先级别高于逗号运算符,因此应先求解a=3*5(也就是把“a=3*5”作为一个表达式)。经计算和赋值后得到a的值为15,然后求解a*4,得60,因此整个逗号表达式的值为60。(3)逗号表达式可以进行嵌套,即表达式1和表达式2本身也可以是逗号表达式。例如:(a=12,a*4),a+5

先求解逗号表达式“a=12,a*4”,a=12,a*4=12*4=48,所以表达式的值为48,然后求解表达式a+5的值,注意在逗号表达式“a=12,a*4”求解过程中变量a的值没有发生变化,所以a=12,a+5=12+5=17,因此整个逗号表达式的值为17。(4)逗号表达式无非是把若干个表达式“串联”起来,按表达式出现的顺序依次求值,从(3)例子的分析过程可知,表达式“(a=12,a*4),a+5”等价于“a=12,a*4,a+5”,即表达式进行顺序求值。因此逗号表达式的一般形式可以扩展为:表达式1,表达式2,表达式3……表达式n

运算的对象可以是任何类型的表达式,整个表达式的值等于表达式n的值。例如:int a,b,c;sa=1,b=a+2,c=b+3;

逗号表达式按照顺序进行求解,a=1,b=a+2=1+2=3,c=b+3=3+3=6,所示该逗号表达式的运算结果为6。该逗号表达式可以用下面三个有序的赋值语句来表示:a=1;b=a+2;c=b+3;

由此看来,逗号运算的使用使程序变得简洁,但逗号表达式不要滥用。在许多情况下,使用逗号表达式的目的只是想分别得到各个表达式的值,而并非一定需要得到和使用整个逗号表达式的值,逗号表达式最常用于循环语句(for语句)中。

例如:for(s=0,i=1;i<=100;s+=i,i++) s=0,i=1:是逗号表达式用来对变量进行初始化。 s+=i,i++:也是逗号表达式用来修改变量的值。

5.7 位运算符

在很多系统程序中常要求在位(bit)一级进行运算或处理,位运算原本属于汇编语言的功能,由于C语言是介于高级语言和汇编语言之间的一种中间语言,是为开发系统软件而设计的,所以它提供了多种类似于汇编语言的功能。C语言提供了位运算的功能,这使得C语言也能像汇编语言一样用来编写系统程序。

位运算是C语言的一种特殊运算功能,它是以二进制位为单位进行运算的,即进行数的二进制位的运算。例如,一个字符型数据占据8个二进制位(1个字节),则最右边的二进制位称为第0位,最左边的二进制位称为最高位。位运算符分为位逻辑运算符、位移位运算符和位自反赋值运算符三种。位运算对象只能是整型(int、short、unsigned、long)或字符型(char)数据。5.7.1 位逻辑运算符

位运算是指对二进制数按位进行运算,其操作对象是一个二进制位集合,每个二进制位只能存放0或1。位逻辑运算符是将数据中每个二进制位上的“0”或“1”看成逻辑值,逐位进行逻辑运算的位运算符。1.位逻辑运算符

位逻辑运算符包括按位非、按位与、按位或和按位异或。具体如表5-8和表5-9所示。表5-8 位逻辑运算符表5-9 位逻辑运算符的运算规则

根据表5-8,表5-9对位逻辑运算符做以下几点说明:(1)按位非(~)运算符是单目运算符,它的运算对象只有一个,它的结合性是自右向左。其功能是对参与运算的数的各二进位按位求反。例如:~0=1,~1=0。(2)按位与运算符(&)是双目运算符,它的结合性是自左向右。其功能是参与运算的两数的二进制各对应位相与。只有对应的两个二进位均为1时,结果位才为1,否则为0。

例如:二进制代码00001001和00000101相与,图示如下:

按位与运算通常用来对某些位清0。根据对应位同为1时才为1的原则,为了使某数的指定位清零,可将数按位与一特定数相与,该数中和为1的位,特定数中对应的位为0;该数中的为0的位,特定数中对应的位为0或1均可。

按位与运算还可以用来取一数中的某些位,对要取的那些位,特定数中相应的位设为1即可。(3)按位或运算符(|)是双目运算符,它的结合性是自左向右。其功能是参与运算的两数的二进制各对应位相或。只要对应的两个二进位有一个为1时,结果位就为1。参与运算的两个数均以补码出现。例如:二进制代码00001001和00000101相或,图示如下:

按位或运算可将数据的某些位置1,将与该数按位或的特定数的相应位置1即可。(4)按位异或运算符(^)是双目运算符,它的结合性是自左向右。其功能是参与运算的两数的二进制各对应位相异或,当两对应的二进位上的数不相同时,结果为1。例如:二进制代码00001001和00000101相异或,图示如下:

按位异或运算是当两个数的对应位不同时,结果为1,所以按位异或运算可以把数的某些位翻转,即0变1,1变0,保留某些位。【实例5-13】位逻辑运算应用举例。#includevoid main( ){int i,j,c1,c2,c3,c4;i=10;j=12;c1=~i;c2=i&jc3=i|j;c4=i^j;printf("~i =%d\n",c1);printf("i&j =%d\n",c2);printf("i|j =%d\n",c3);printf("i^j =%d\n",c4);}

程序运行的结果如图5-7所示。图5-7 程序运行结果【例题分析】

在第1章数制的转换与存储一节中,我们知道数据在计算机中是以二进制补码的形式存储的。对于一个正数,原码是将该数转换成二进制,它的反码和补码与原码相同。对于一个负数,原码是将该数按照绝对值大小转换成的二进制数,最高位即符号位为1;它的反码是除符号位外将二进制数按位取反,所得的新二进制数称为原二进制数的反码;它的补码是将其二进制的反码加1。

10的二进制补码为00001010,12的二进制补码为00001100(这里为了表示的方便,用八位二进制来表示,实际的运算过程中,在Visual C++ 6.0环境下,整型数值占4个字节,即用32位二进制表示)。

~i表示对二进制00001010进行按位取反运算,即0变1,1变0,所以~00001010=11110101,11110101是一个整型数的补码形式,转换成十进制为-11。

i&j表示00001010与00001100进行按位相与,结果为00001000,最高位为0,表示是一个正数,原码、反码和补码相同,将其转换成3二进制为:2=8。

i|j表示00001010与00001100进行按位相或,结果为00001110,最高位为0,表示是一个正数,原码、反码和补码相同,将其转换成321二进制为2+2+2=14。

i^j表示00001010与00001100相异或,结果为00000110,最高位为0,表示是一个正数,原码、反码和补码相同,将其转换成二进制21为2+2=6。2.位逻辑运算符的优先级

在位逻辑运算符中优先级如图5-8所示。图5-8 位逻辑运算符

与前面已经学习过的其他运算符相比,位逻辑运算符的优先级如图5-9所示。图5-9 位逻辑运算符5.7.2 移位运算符

移位运算是将数据看成二进制数,对其进行向左或右移动若干位的运算。移位运算包括左移位运算和右移位运算。1.移位运算符

移位运算符包括左移位运算符和右移位运算符,都是双目运算符,有两个运算对象。第一个运算对象是要移位的运算对象,第二个运算对象是所移的二进制位数。移位运算符的表示形式、举例和功能如表5-10所示。表5-10 移位运算符

对于移位运算符做以下几点说明:(1)左移运算符是将一个数的二进位全部左移若干位。“<<”左边的数是要移位的数,右边的数指定移动的位数。

左移的规则是将二进制数向左移动若干位,左边移走的高位被丢弃,右边被空出来的低位补零。移位运算符的操作对象为整型,有无符号数和带符号数之分,但对于左移运算来说,若高位左移后溢出,则舍弃,低位补0。例如:a=a<<2,将a的二进制数左移2位,右补0。若a=13,即二进制数00001101,则

最后a=52。(2)右移运算符是将一个数的二进位全部右移若干位。“>>”左边的数是要移位的数,右边的数指定移动的位数。

右移的规则是将二进制数向右移动若干位,右移与被移位的数据是否带符号有关。对于无符号整数来讲,左端空出的高位全部补0。而对于有符号的数来讲,如果符号位为0(即正数),则被空出来的高位部分补零。如果符号位为1(即负数),则被空来的高位部分补0还是补1,与使用的计算机系统有关,有的计算机系统补0,有的计算机系统补1。例如:a=a>>2,将a的二进制数右移2位,左补0。若a=13,即二进制数00001101,则

最后a=3。

若a=-15,即计算机内的二进制表示为11110001,则

最后a=-4。11111100是-4在计算机内以其二进制补码的表示形式(这个结果是使用Visual C++ 6.0环境运行的结果)。2.移位运算的优先级

如果在一个式子中出现多种运算符,要按照运算符的优先级顺序进行运算,移位运算符在位运算符中的优先顺序如图5-10所示。图5-10 移位运算符的优先级

移位运算符与前面学习过的几种运算符混合使用时的运算优先顺序如图5-11所示。图5-11 移位运算符的优先级5.7.3 位自反赋值运算符

在前面学习赋值运算符与其表达式的时候,我们已经提到过位自反赋值运算符。复合赋值运算符是由某个规定的运算符和基本赋值运算符组合而成的,当组成复合赋值运算符中的“某个规定的运算符”是位运算符时,复合赋值运算符就称为位自反赋值运算符。具体如表5-11所示。表5-11 位自反赋值运算符

位自反赋值运算符与其他赋值运算符的优先级相同,结合性也是自右向左。位自反赋值运算符的运算规则与算术自反运算符的运算规则相同,详细介绍请参考算术自反运算符的内容。

5.8 位运算符

在C语言中,长度运算符用于测试数据类型符(或变量)所分配的内存字节数,用于测试的对象数据必须用圆括号括起来。和其他运算符不同的是,长度运算符是由一个关键字sizeof表示的,它用于计算一种数据类型所占用的字节数。不论是基本的数据类型还是复杂的构造类型,都可以用它来计算。表示格式为:sizeof(类型说明符或变量)

长度运算符的表示形式、举例和功能如表5-12所示。表5-12 长度运算符

对长度运算符做以下几点说明:(1)长度运算符(sizeof)是一个单目运算符,它的功能是返回给定类型的运算对象所占内存字节的个数,因此长度运算的结果是一个整型数。例如:sizeof(char)

计算一个字符类型数据占用的字节数,结果为1,表示字符类型数据在内存中占用一个字节的空间。sizeof(double)

计算一个双精度浮点型数据的长度,结果为8,表示双精度浮点类型数据在内存中占用4个字节的空间。(2)长度运算符的运算对象除了数据类型符说明符以外,还可以是数组名或表达式等。如果运算对象是一个表达式(如常量、变量、数组名、结构体变量、共用体变量等),则sizeof()不会对表达式求值,只给出该表达式所占用的字节数。例如:sizeof(100)=4 /*在Visual C++ 6.0环境下整型数据占4个字节*/sizeof('a')=1 /*求字符型常量'a'的长度*/sizeof(struct ABC) /*求结构类型ABC的长度*/sizeof(a) /*求变量a的长度,即它所占用的字节数*/(3)长度运算符的优先级与其他单目运算符如~、!、++、——是同级别的,结合性是自右向左。

5.9 本章小结

C语言提供十分丰富的运算符,一共13类,34种运算符。本章对算术运算符、赋值运算符、关系运算符、逻辑运算符、条件运算符、逗号运算符、位运算符、长度运算符以及它们的表达式进行了讲解,对各种运算符优先级和结合性做了明确的说明。运算符的总结如附录B所示。

5.10 习题

1.算术运算符、赋值运算符和关系运算符的运算优先级按从高到低依次是什么?

2.对C程序在做逻辑运算时判断操作数真、假的表述,下列哪一个是正确的?(  )

A.0为假非0为真

B.只有1为真

C.-1为假1为真

D.0为真非0为假

3.设有整型变量i, j, k,i值为3,j值为6。计算表达式 k=i ^ j<<3; 后,k的值是多少?

4.ch是字符变量,判断ch是英文字母的逻辑表达式是什么?

5.设x和y均为int型变量,且x=1,y=2,则表达式1.0+x/y的值是多少?

第6章 输入与输出

数据的输入与输出是一个算法所具有的特点。任何高级语言必须有数据的输入和输出功能,C语言本身不提供输入和输出语句,输入与输出操作是由函数来实现的。在C标准函数库中有一些输入/输出函数,可在程序中直接调用,如printf()函数和scanf()函数,它们不是C语言文本的组成部分,而是以函数库的形式存放在系统之中。C语言程序是由函数构成的,而函数是由各种各样的语句组成的。本章将介绍C语言中的各种语句及数据输入与输出。本章内容如下: C语句概述; 输入与输出函数; 整型数据的输入与输出; 浮点型数据的输入与输出。

6.1 C语句概述

一般的C语言语句包括数据描述和数据操作两部分。数据描述由声明部分完成,如变量定义等,没有操作;数据操作是由语句来实现的,即程序的执行部分,程序的功能也是由执行语句实现的。6.1.1 流程控制语句

从程序流程的角度来看,程序可以分为三种基本结构,即顺序结构、分支结构、循环结构。这三种基本结构可以组成所有的各种复杂程序。C语言提供了多种语句来实现这些流程的结构。流程控制语句用于控制程序的流程,用于实现程序的各种结构方式,它们由特定的语句定义符组成。C语言有9种控制语句,如表6-1所示。表6-1 流程控制语句

根据表6-1可大致将流程控制语句分为三类。1.条件判断语句 if…else语句 switch…case语句

根据条件是否成立,选择执行程序。例如,使用if…else语句来判断两个数的大小,代码如下:if(a>b) /*如果a大于b*/{printf("%d",a); /*输出a的值*/}else{printf("%d",b); /*否则,输出b的值*/}

使用switch…case语句对学生的成绩划分区间,代码如下:switch(grade) /*判断grade的数值*/{case 'A': /*如果grade的值为A*/printf("85~100\n"); /*说明分数在85~100之间*/break; /*跳出循环*/case 'B': /*如果grade的值为B */printf("70~84\n"); /*说明分数在70~84之间*/break; /*跳出循环*/case 'C': /*如果grade的值为C*/printf("60~69\n"); /*说明分数在60~69之间*/break; /*跳出循环*/case 'D': /*如果grade的值为D*/printf("<60\n"); /*说明分数小于60分*/break; /*跳出循环*/default: printf("Error\n"); /*如果grade的值不是A、B、C、D中的一个,那么输出error*/}2.循环执行语句 for(表达式1;表达式2;表达式3)语句 while(条件表达式)语句 do{}while(条件表达式)语句

当给定的条件满足时,反复执行某段程序,直到条件不成立为止。

例如:使用for语句实现连续整数1~10求和运算,代码如下:int s,i;for(i=1;i<=10;i++){s+=i;}

使用while语句实现连续整数1~10求和运算,代码如下:int s,i;while(i<=10){s+=i;i++;}

使用do…while语句实现连续整数1~10求和运算,代码如下:int s,i;do{s+=i;i++;}while(i<=10)3.转向语句

break语句、continue语句、return语句、goto语句。(1)break语句

break语句常用于使流程跳出switch结构,继续执行switch语句下面的一个语句。如上面switch语句中用break语句来结束switch语句;break语句还用来从循环体内跳出,break语句用于强制结束执行的程序。【实例6-1】break语句的使用。该段程序实现的功能是查找100以内,能同时被3和7整除的最小整数。#includevoid main() /*主函数*/{int j; /*定义变量*/for(j=1;j<100;j++) /*设置循环条件*/{if(j%3==0&&j%7==0) /*判断j的值是否能同时被3和7整除*/{printf("%d\n",j); /*输出j的值*/break; /*跳出循环*/}}}

程序运行的结果为:21。

当变量j从小到大变化时,在循环体内部进行检查,当if条件不满足时,不执行if下面用“{ }”括起来的内容,然后执行下一次的循环;当if条件满足时,将这个整数输出,并用break语句强制跳出循环体,for循环结束。

需要注意的是break语句只能跳出一层循环,即从当前的循环层中跳出。break语句不能用于循环语句和switch结构语句之外的任何其他语句中。(2)continue语句

continue语句只用于循环语句中,其作用是结束本次循环,不再执行循环体中的continue语句之后的语句,然后跳转到循环的开始处,进行下一次是否执行循环语句的条件判断。【实例6-2】continue语句的使用。该程序实现的功能是查找100以内,所有能同时被3和7整除的整数。#includevoid main() /*主函数*/{int j; /*定义变量*/for(j=1;j<100;j++) /*设置循环条件*/{if(j%3==0&&j%7==0) /*判断j的值是否能同时被3和7整除*/{printf("%d ",j); /*输出j的数值*/continue; /*继续查找*/}}}

程序运行的结果为:21 42 63 84

当变量j从小到大变化时,在循环体内部进行检查,当if条件不满足时,不执行if下面用“{ }”括起来的内容,然后执行下一次的循环条件的判断;当if条件满足时,将该整数输出,使用continue结束本次循环,进行下一次循环的判断。

通过【实例6-1】和【实例6-2】的比较,可以看出continue语句与break语句的区别。continue语句只是结束循环结构中的本次循环,而不是跳出整个循环过程。实际上在【实例6-2】中可以完全不使用continue语句,可以得到同样的结果,因为使用continue语句结束本次循环,使用continue语句后面的语句无法执行,【实例6-2】中的continue语句后面已经没有语句,所以可以不用。(3)goto语句

goto语句被称为无条件转移语句,它的一般形式为:goto 语句标号;

其中,语句标号用标识符表示,它的命名规则与变量名相同,由字母、数字、下画线组成,且第一个字符必须为字母或下画线。例如:goto loop;goto laber_1;

这些都是合法的语句标号。goto a-b;goto 2ab;

这些都是不合法的语句标号。

goto语句可以与if语句一起构成循环结构。【实例6-3】goto语句的使用。该程序的功能是使用goto语句和if语句构成循环结构,求100以内整数的和。#includevoid main( ) /*主函数*/{int i,sum; /*定义递增变量i及用于保存整数和的sum*/i=1; /*初始化i的值*/sum=0; /*初始化sum的值*/loop: if (i<=100) /*设置loop标号*/{sum=sum+i ;i++;goto loop; /*跳至loop标号处*/}printf("sum=%d",sum); /*输出sum的最终值*/}

程序运行的结果为:sum=5050

loop是一个语句标号,当程序执行到goto loop语句的时候,程序将跳转到loop所标识的那一行程序即if语句。

C语言中提供了for语句、while语句、do while语句这三种循环语句,使用方便,因此我们不提倡用goto语句来构造循环结构。还可以用goto语句将程序强制转移到指定语句(例如从循环体内跳到循环体外)一般用于跳出多层循环。前面知道break只能跳出单层循环,如果要跳出多层循环,就要用到goto语句。

在结构化程序设计中,goto语句尽量少用,因为goto是将程序强制转移,比较自由随意,这不利于结构化程序设计,滥用它会使程序流程无规律、可读性差。6.1.2 函数调用语句

函数调用语句是由函数名、实际参数列表组成的,以分号结尾。其一般形式为:函数名(实际参数表);

执行函数语句就是调用函数体并把实际参数赋予函数定义中的形式参数,然后执行被调用函数体中的语句,求取函数值。例如:printf(“how are you!”);

调用C语言中的标准输出函数,用来输出一个字符串。getchar();

调用C语言系统的标准库函数,字符输入函数,用来输入一个字符。max(a,b);

调用用户自定义函数。关于函数的内容将在第12章中进行介绍。6.1.3 表达式语句

表达式后面加上一个分号就构成了一个表达式语句。在第5章中我们学习了算术表达式、关系表达式、逻辑表达式、赋值表达式等,任何表达式加上一个分号都可以构成表达式语句,执行表达式语句就是计算表达式的值。a=4; /*赋值表达式语句*/i++; /*自增运算表达式语句*/a+=b+c; /*复合赋值表达式语句*/~ i | j>>3; /*位运算表达式语句*/'x'+1>c , -a-5*b<=d+1; /*逗号表达式语句*/

C语言中的语句大部分都是表达式语句。6.1.4 空语句

空语句又称空操作语句,只有一个分号,在程序执行过程中不做任何操作。空语句的作用:(1)在循环语句中使用空语句提供一个不执行操作的空循环体;(2)为有关语句提供标号,用以说明程序执行的位置。while(getchar()!='\n') ; /*空语句*/

这里的循环体由空语句构成。该语句的功能是,只要从键盘输入的字符不是回车换行则重新输入。空语句的存在只是语法完整性的需要,其本身并不代表任何动作。6.1.5 复合语句

把多个语句用括号“{ }”括起来组成的一个语句称复合语句。一般语句格式为:{语句1;语句2;…语句n;}

复合语句从形式上看是多个语句的组合,但在语法意义上它是一个整体,相当于一条语句,所以凡是可以用简单语句的地方都可以用复合语句来实现。在程序设计中复合语句被看成是一条语句,而不是多条语句。例如:

for循环语句的循环体使用复合语句。for(i=1;i<=10;i++){z=x+y;t=z/100;printf("%d",t);}

if条件语句中使用复合语句。if (a>b){a=a+b;b-=a;}

复合语句内的各条语句都必须以分号“;”结尾,在括号“}”外不需加分号。组成复合语句的语句数量不限,一个语句可以占多行,一行也可以有多个语句。在复合语句中不仅有执行语句,还可以说明变量。例如:{int c; /*定义变量*/c=getchar();putchar(c);}

复合语句组合多个子语句的能力及采用分程序定义局部变量的能力是C语言的重要特点,它增强了C语言的灵活性,同时还可以按层次使变量作用域局部化,使程序具有模块化结构。

6.2 输入与输出函数

C语言程序是由函数构成的,输入与输出操作都是由函数来实现的。C语言中没有提供输入的语句,数据的输入与输出是利用系统函数来完成的。在C语言标准函数库中有一些输入/输出函数,可以在程序中直接调用,其中包括:scanf()(格式输入函数)、printf()(格式输出函数)、getchar()(字符输入函数)、putchar()(字符输出函数)、gets()(字符串输入函数)、puts()(字符串输出函数)。

需要注意的是,这些不是C语言文本的组成部分,而是以函数库的形式存放在系统中的,因此在使用标准I/O库函数之前,需要用编译命令#include将有关“头文件”包括到源文件中,为源程序的编译提供相关的信息。

使用标准输入/输出库函数时要用到“stdio.h”头文件,因此源程序的开头应有以下预编译命令:#include< stdio.h >或#include “ stdio.h”

在Tubro C中考虑到printf()和scanf()函数频繁使用,系统允许在使用这两个函数时可不加#include< stdio.h >或#include "stdio.h",但是在Visual C++ 6.0环境下输入/输出函数在使用前,源程序开头必须引入#include< stdio.h >或#include "stdio.h"头文件。6.2.1 格式输出函数

在前几章中我们已经初步接触到格式输入与格式输出函数printf()和scanf()函数,它们是C语言标准I/O库函数中最简单、最常用的函数。

printf()函数我们在前面已经多次用到,printf()函数是格式控制输出函数,使用的一般格式为:printf("格式控制字符",输出项列表);

printf()函数的作用是按照自右向左的顺序,依次计算“输出地址列表”中表达式的值,然后按照“控制格式字符”中规定的格式输出到显示器上。

printf()函数有两个参数“格式控制字符”和输出项列表,“格式控制字符”是由控制输出格式字符等字符组成的字符串。输出项列表是用逗号分隔的若干个表达式。

例如:printf("a=%d,b=%f,c=%c\n",a,b,c);

其中,"a=%d,b=%f,c=%c\n"是格式的控制部分,a,b,c是输出项序列,是用逗号分隔开的三个变量。1.printf()函数的格式控制字符“格式控制字符”是用双引号括起的一串字符,包括格式说明、普通字符和转义字符三种。格式控制字符的功能是指定输出数据的格式和类型。例如:printf("a=%d\n",a);

其中,“a=”是普通字符,“%d”是格式说明符,“\n”是转义字符,而a是输出项。

格式说明符由%和格式字符组成。作用是转换输出数据的格式,表示对数据输出格式的控制,如%d,%f等。它与后面的数据输出项对应,即格式说明与数据输出项的数据个数、数据类型及数据排放次序相匹配。例如:printf("%d,%f",a, x)

格式说明符“%d”与输出项“a”对应,表示将控制数据输出项“a”按格式说明“%d”规定的格式输出,即按十进制整数的形式输出。“%f”与输出项“x”对应,表示将控制数据输出项“x”按格式说明“%f”规定的格式输出,即按浮点型数据的形式输出。下面介绍几种常用的格式字符。 d格式字符,输出十制整数。

例如:int a=122;printf("a=%d",a);

输出的结果为:a=122。 x(或X)格式字符,输出十六进制整数。例如:int a=122;printf("a=%x",a);

输出的结果为:a=7A。 o(或O)格式字符,输出八进制整数。例如:int a=122;printf("a=%x",a);

输出的结果为:a=172。 U格式字符,输出不带符号的十进制整数。例如:int a=122;printf("a=%d",a);

输出的结果为:a=122。 c格式字符,输出单个字符。例如:int a=98;printf("%c",a);

输出的结果为:b,为一个字符。 s格式字符,输出字符串。char s[]="hello!";printf("%s",s);

输出的结果为:hello! f格式字符,输出十进制单、双精度数。例如:float a=314.1;printf("a=%f",a);

输出的结果为:a=314.100006 e(或E)格式字符,输出指数形式的实数型数。例如:float a=314.1;printf("a=%e",a);

输出的结果为:a=3.141000e+002 g(或G)格式字符,输出指数形式的实数型数不带无效0的浮点

数。例如:float a=314.1;printf("a=%e",a);

输出的结果为:a=314.1 %格式字符,输出一个百分号。例如:float a=12.56;printf("a=%f%%",a);

输出的结果为:a=12.560000%

转义字符是以“\“开头和其他特殊字符组合而成的具有一定含义的字符。转义字符可以在printf()函数中使用,在前面我们已经讲解了几种常用的转义字符,在这里不再赘述。

普通字符是指在“格式控制字符”部分,除了“格式说明符”和“转义字符”之外的其余字符均为普通字符,在输出时普通字符被原样输出。

例如:int x=3, y=6, z=0;printf("x=%d,y=%d,z=%d",x,y,z);

输出的结果为:x=3, y=6, z=0“x=”、“ ,”、“ y=”、“ z=”这些都是普通字符,在输出时被原样输出。2.printf()函数输出项列表

输出项列表是需要输出的一些数据。判断输出项列表有以下几点说明: 输出项列表的数据可以有一个或多个,也可以没有。printf()函数

允许没有输出项列表部分,这表示输出一个字符串。例如:printf("请输入一个正整数:");

输出的结果为:请输入一个正整数:

上面的那行语句里面只有普通字符,因此在输出的时候只是原样输出。 输出项列表可以是多个输出项,各个输出项之间用逗号“,”分

隔。例如:printf("%d*%d+%d*%d=%d\n",x,x,y,y,1989); printf()函数中格式控制部分的“格式说明符”和“输出项列表”

在个数和和类型上必须一一对应。例如:printf("a=%d,b=%f\n",a,b);

a与%d对应,%d 表示以十进制整数的形式输出,相对应的a是一个整型变量。b与%f对应,%f表示以浮点型数输出,相对应的b是一个浮点型变量。 输出项列表可以由变量、常量或表达式组成。如果是常量,直接

用常量代替“格式控制字符”里面的“格式字符”;如果是变量,

则用变量里面存储的值来取代“格式控制字符”里面的“格式字

符”;如果是表达式,则先对表达式进行运算,然后用它的运算

结果取代“格式控制字符”里面的“格式字符”。例如:printf("%f",3.5+56%10+3.14);

输出的结果为:12.640000【实例6-4】printf( )函数应用举例。#includevoid main( ) /*主函数*/{/*输出星号*/printf(" *\n");printf(" * *\n");printf(" * * *\n");printf("* * * *\n");printf(" * * *\n");printf(" * *\n");printf(" *\n");}

程序运行的结果如图6-1所示。图6-1 程序运行6.2.2 格式输入函数

scanf()函数是格式控制输入函数,scanf()函数使用的一般格式为:scanf("格式控制字符",输入项列表);

scanf()函数有“格式控制字符”,“输入项列表”两个参数,scanf()函数的功能是按照“格式控制字符”中规定的输入格式,从键盘上读取若干个数据,按“输入项列表”中变量从左向右的顺序,依次存入对应的变量中。

scanf()函数的格式控制字符基本上与printf()函数的格式控制相似,都是以 % 开始,以一个格式字符结束,格式控制的功能是规定输入数据的格式。

关于scanf()函数,做如下几点说明:(1)在scanf()函数中一个格式说明要求输入一个数据,就必须在地址列表中有一个变量的地址与之对应,并且类型要一致。例如:scanf("%d%f",&a,&b);

其中,“%d”与&a对应,变量a的类型为整型,“%f”与&b对应,变量b的类型为浮点型。(2)scanf()函数的输入项必须是一个“地址”,它可以是一个变量的地址,也可以是数组的首地址,但不能是变量名。“&”是取地址符号,变量的地址必须写成“&变量名”。例如:scanf("%d,%d",&a,&b);

&a、&b分别表示变量a、b存储单元的地址。(3)scanf()函数格式说明符与printf()函数相似,格式说明符与输入项列表一一对应。常用到的格式说明符有以下几种。 D:输入有符号的十进制整数; o:输入无符号的八进制整数; u:输入无符号的十进制整数; x或X:输入无符号的十六进制整数; c:输入单个字符; s:输入字符串; f:输入单精度或双精度数。

在使用scanf()函数时,普通字符和转义字符可不出现。(4)格式说明符中有普通字符或转义字符时,数据输入时,必须按它们的原样输入。例如:int a,b;scanf("%d,%d",&a,&b);

在输入数据时,两个数据之间必须用“,”开隔。如:4,6↙

才是正确的输入方式,表示将整数4存放在变量a所代表的存储空间内,将整数6存放在变量b所代表的存储空间内。又如:int a,b;scanf("a=%d,b=%d",&a,&b);

正确的数据输入格式为:a=4,b=6↙(5)“格式控制字符”中如果没有任何普通字符,输入数据时,各个数据之间可以用空格、跳格键(Tab)或回车键作为间隔符。例如:int a,b;scanf("%d%d",&a,&b);

可以有三种输入方式:

① 数据中间使用一个或多个空格10 20↙10 20↙

② 数据中间按下跳格键(Tab)10[Tab]20↙

③ 数据中间按下回车键,即分行输入10↙20↙

另外,上面几种方式可以混合使用。例如:int a,b,c,d;scanf("%d%d%d%d",&a,&b,&c,&d);

输入时:10 20↙30[Tab]40↙(6)可以指定输入数据所占的列数,系统自动按指定的列数截取所需的数据。【实例6-5】指定输入数据列数。#includevoid main() /*主函数*/{int a,b; /*定义变量*/scanf("%2d%3d",&a,&b); /*输入*/printf("a=%d,b=%d ",a,b); /*输出*/}

输入:123456↙

程序运行的结果为:a=12,b=345

同样,也可以应用于输入格式说明符为字符型的输入语句中。【实例6-6】指定输入数据列数。#includevoid main(){char a,b; /*定义两个字符型变量*/scanf("%2c%3c",&a,&b); /*输入*/printf("a=%c,b=%c",a,b); /*输出*/}

输入:ABCDEFG↙

程序运行的结果为:a=A,b=C

在上面的程序中,分别为字符型数据a,b指定了输入的字符序列,即将AB给了变量a,将CDE给了变量b,而由于字符型数据只能存储一个字节的数据,因此a=A,b=C。(7)输入数据时不能规定精度。例如:scanf("%5.2f",&x);

这种输入格式是错误的。(8)scanf()函数在进行输入的时候是没有提示的。例如:scanf("a=%d",&a);

在运行时,不会像printf()函数那样将“a=”原样显示。运行时应输入:a=10↙scanf("%d,%d",&a,&b);

应输入:10,3↙

若需要提示时,可以用printf()函数输出字符串进行提示。【实例6-7】使用scanf()函数输入两个数,输出其中较大的那个数。#includevoid main(){int x,y,max; /*定义所需变量*/printf("请输入两个整数:\n"); /*输出提示文字*/scanf("%d,%d",&x,&y); /*输入x和y的值*/if(x>y) /*判断x和y的值*/{max=x;}else{max=y;}printf("两个数中较大的数为:%d\n",max); /*将比较后的结果输出*/}

程序运行时,出现一行提示信息:请输入两个整数:

输入:4,10↙

输出:两个数中较大的数为:10(9)如果输入的数据多于scanf()函数所要求的个数,余下的数据可以被下一个scanf()函数接着使用。【实例6-8】scanf()函数输入多余的数据,可被下一个scanf()函数使用。#include void main(){int a,b,c; /*定义所需变量*/scanf("%d%d",&a,&b); /*首先输入a和b的值*/scanf("%d",&c); /*然后输入c的值*/printf("a=%d,b=%d,c=%d",a,b,c); /*将三者的数值输出*/}

输入:12 34 56 78↙

程序运行的结果为:a=12,b=34,c=56

在上面的程序中出现两个scanf()输入函数,第一个scanf()只读入了前两个数据12和34,剩下的56将被下一个scanf()函数所用。(10)在scanf()函数中某格式字符读入数据时,遇以下情况时认为该数据结束: 遇“数据分隔符”,如空格、回车、tab或指定字符。 遇宽度结束,如“%3d”,只取3列后读取结束。 遇非法输入。【实例6-9】应用举例。#include void main(){int a,b;scanf("%d%d",&a,&b);printf("a=%d,b=%d",a,b);}

输入:123.45 678↙

程序运行的结果为:a=123,b=-858993460

程序运行时,将123送a,而b没有被赋值,因为整型数据中不能出现小数点,但b=-858993460,是因为在对变量进行定义而不初始化时,系统会为变量分配一个随机值。(11)需要注意的是,在使用格式说明符%c输入一个字符时,凡是从键盘输入的字符,包括空格、回车等都被作为有效字符接收。【实例6-10】应用举例。#include void main(){char ch1,ch2,ch3;scanf("%c%c%c",&ch1,&ch2,&ch3);printf("ch1=%c,ch2=%c,ch3=%c",ch1,ch2,ch3);}

当输入:A B C↙

此时字符'A'存入ch1所代表的存储单元,'□'(空格)作为字符存入ch2代表的存储单元,字符'B'存入ch3代表的存储单元。

输入:A123B↙

字符'A'送入ch1,字符'1'送入ch2,字符'2'送入ch3。(12)如果在%后面有个“*”附加说明符,表示跳过它指定的列数。【实例6-11】应用举例。#include void main(){int a,b;scanf("%2d□%*3d□%2d",&a,&b);printf("a=%d,b=%d",a,b);}

输入:12 345 67↙

变量赋值结果为:a=12,b=676.2.3 字符输入与字符输出函数

当用scanf()函数和prinft()函数对字符型数据进行输入与输出时,格式说明符要使用“%c”,在C语言中对字符型数据还专门提供了字符输入函数(getchar())与字符输出函数(putchar())。1.getchar()函数

getchar()函数是字符输入函数,它的功能是从键盘上输入一个字符。一般形式为:getchar( )

getchar()从外部设备读取一个字符到内部程序中,因此需要有一个变量接收该字符。一般使用字符型或整型变量。通常使用的方式是采用一个赋值语句:变量= getchar( );

例如:char c;c=getchar();

运行时输入字符'A',则将输入的字符'A'赋予字符变量c。

对于字符输入函数,做以下几点说明:(1)使用putchar()函数前必须要包含头文件stdio.h。stdio.h是基本输入/输出的头文件。C语言中的输入/输出函数是以函数库的形式存放在系统中的,在使用标准I/O库函数之前,需要用编译命令#include将有关“头文件”包括到源文件中,为源程序的编译提供相关的信息。

使用文件包含命令:#include或#include "stdio.h"(2)getchar()函数只能接收单个字符,输入数字也按字符处理。输入多于一个字符时,只接收第一个字符,程序中没有使用完的字符,将会自动保留下来,留做下次使用。【实例6-12】应用举例。#includevoid main(){char c1; /*定义一个字符型变量c1*/int a; /*定义一个整型变量a*/c1=getchar(); /*输入c1的值*/a=getchar(); /*输入c2的值*/printf("c1=%c,a=%d",c1,a); /*输出各自的结果*/}

当输入:20↙

程序运行的结果为:c1=2,a=48

此时变量c1中将获得字符'2',变量a中将获得字符'0'的ASCII值48。(3)使用scanf()函数的也可以使用输入字符,getchar()和scanf(“%c”)的功能是一样的。【实例6-13】getchar()和scanf()的使用。#includevoid main(){char c1,c2; /*定义两个字符型变量*/c1=getchar(); /*输入c1的值*/scanf("%c",&c2); /*输入c2的值*/printf("c1=%c,c2=%c",c1,c2); /*输出各自的值*/}

输入:ad↙

程序运行的结果为:c1=a,c2=d2.putchar()函数

putchar()函数为字符输出函数,它的作用是在显示器上输出一个字符。其一般形式为:putchar(参数);

其中,这里的参数可以是字符型变量或整型变量,也可以是字符型常量或表达式。

例如:char c='A';putchar(c);

函数参数是一个字符型变量,结果是输出字符变量c的值,即大写字母A。int a=98;putchar(a);

函数参数是一个整型变量,结果是输出整型变量a的值ASCII码表里所对应的字符,即小写字母b。putchar('b');

函数参数是一个字符型常量,输出字符常量b。char c='1';putchar(c+4);

函数参数是一个整型表达式,字符c的ASCII码值为49,先对表达式进行运算,即c+4='1'+4=49+4=53,ASCII码表中对应的字符为'5',因此输出的结果为'5'。Tips 需要注意的是,使用putchar函数与getchar()函数相似,在使用前必须要加上包含头文件stdio.h。【实例6-14】putchar()函数的应用。#include void main(){char c1='A',c2='B',c3='C'; /*定义三个字符型变量并分别赋值*/putchar(c1); /*输出c1的值*/putchar(c2); /*输出c2的值*/putchar(c3); /*输出c3的值*/putchar('\t'); /*输出一个Tab键的空隙*/putchar(c1); /*输出c1的值*/putchar(c2); /*输出c2的值*/putchar('\n'); /*换行*/putchar(c2); /*输出c2的值*/putchar(c3); /*输出c3的值*/}

程序运行的结果为:ABC ABBC

6.3 整型数据的输入与输出

整型数据的输入与输出通常使用printf()、scanf()函数,函数参数中格式控制符使用整型数据的格式说明符,输入或输出列表使用整型常量、整型变量或整型表达式。6.3.1 整型数据的输出

整型数据格式说明符有以下几种。 d:有符号的十进制整数; o:无符号的八进制整数; u:无符号的十进制整数; x或X:无符号的十六进制整数。(1)d格式符,表示十进制整数(有符号数)。 用法:%d、%ld、%md、%lmd。 说明:%d表示按整数的实际长度输出,%ld表示长整型,可以

输出long型数据。

格式说明中,在“%”和格式说明符间可以插入以下附加符号。M表示一个正整数,用来控制输出数据的宽度。

对于%md或%lmd,如果m大于输出数据的宽度,则前面补空格;如果m小于输出数据的宽度,则按数据的实际长度输出。

对于%-md或%-lmd,如果m大于输出数据的宽度,则后面补空格;如果m小于输出数据的宽度,则按数据的实际长度输出。【实例6-15】应用举例。#include void main(){int a=123;printf("%d,%2d,%5d,%-5d,",a,a,a,a);}

程序运行的结果为:123,123, 123,123

%d将数据按实际长度输出,即123;%2d指定的字段宽度为2,小于数据的宽度,因此按原数据宽度输出;%5d指定的字段宽度为5,大于数据的宽度,在数据的前面补两个空格;%-5d与%5d基本相同,但是空格补在数据的后面。(2)o格式符,表示八进制无符号整数(o必须小写),没有表示八进制的前导0。

用法:%o、%lo、%mo、%lmo

说明:%lo表示八进制长整型,m指定输出字符的宽度。【实例6-16】应用举例。#include void main(){int a=-1;printf("%d,%o",a,a);}

程序运行的结果为:-1,37777777777

在Visual C++ 6.0环境下,int型数据在内存中占4个字节。(3)X|x格式符,表示十六进制整数,没有表示十六进制的前导0x。

用法:%x、%X、%lx【实例6-17】应用举例。#include void main(){int a=-1;printf("%d,%x,%X",a,a,a);}

程序运行的结果为:-1,ffffffff,FFFFFFFF(4)u格式符,表示无符号的十进制整数。

用法:%u【实例6-18】应用举例。#include void main(){int a=-1;printf("%d,%u",a,a);}

程序运行的结果为:-1,42949672956.3.2 整型数据的输入

整型数据的输入使用scanf()函数,函数参数使用整型数据格式说明符。如d、o、x等。但通常情况下,整型数据格式说明符一般使用d,便于从键盘上输入。“%d%d%d”表示按十进制整数格式输入数据,两个数据之间用一个或多个空格、回车键Enter或跳格键Tab分隔都是合法的,但不能用“;”或其他不符合其格式的间隔符。只有当格式为“%d,%d,%d”时,才能用“,”作为间隔,即要求输入数据的格式必须与“格式控制”的格式完全一致。

在输入整型数据时,scanf()函数中不能有提示,为了能够更加清楚所输入的数据,可以用printf()予以提示。例如:int a,b;printf("请输入两个数:\n");scanf("%d%d",&a,&b);

整型数据的输入在本章的格式输出一节已经详细讲解过。

6.4 浮点型数据的输入与输出

浮点型数据的输入与输出同样用格式输入/输出函数scanf()、printf()。

浮点型数据的输出格式控制符有以下几种:(1)f格式符,表示实数型(单精度、双精度),即以小数形式输出。

用法:%f、%mf、%.nf、%m.nf

说明:对于浮点型数据,在格式说明中,“%”和格式说明符间可以插入以下附加符号。 m:整型数据相同,为输出的数据指定宽度。 n:是一个正整数,表示输出的实数保留n位小数。【实例6-19】应用举例。#include void main(){float x=314.15;printf("%f,%7.2f,%12f,%-12f,%.2f",x,x,x,x,x);}

程序运行的结果为:314.149994, 314.15, 314.149994, 314.149994 ,314.15

浮点型变量所赋的值为314.15,但程序运行使用%f输出时,却为314.149994,这是由于实数在内存中的存储误差导致的。并非所有的数字都是有效数字。单精度实数的有效位数一般是7~8位,双精度实数的有效位数一般是15~16位。(2)e(或E)格式符,使用指数格式表示实数型(单精度、双精度)数据。

用法:%e、%me、%m.ne、%.ne

说明:这里的m、n与上面的意义相同。【实例6-20】应用举例。#include void main(){float x=135.62,y=451.628e-5;printf("%e,%14E,%10.2e",x,-y,x);}

结果:1.356200e+002,-4.516280E-003,□1.36e+002(3)g格式符,用来输出实数型(单精度、双精度),根据数值的大小自动选择占用宽度最小的一种,不输出无意义的零。

用法:%g【实例6-21】应用举例。#include void main(){float x=123.456;printf("%g,%f,%e",x,x,x);}

结果:123.456,123.456001,1.234560e+002

对于浮点型数据的输入格式控制符可以是%f、%e,输入格式应该尽可能简单。可以指定输入数据所占的列数,系统自动按指定的列数截取所需的数据。但是浮点型数据输入时不能规定精度。例如:scanf(“%5.2f”,&x);

这种输入格式是错误的。

6.5 本章小结

本章在第1章、第2章的基础上,进一步介绍C语言编写程序所必需的一些基本知识。详细讲解了C语言程序设计中常用的5种语句:流程控制语句、函数调用语句、表达式语句、空语句和复合语句,并通过实例进行分析。对输入/输出函数进行举例说明,在此基础上,分别对整型数据的输入/输出、浮点型数据的输入/输出做了详细的讲解、补充,使每一种数据类型的输入与输出更加详细具体。

6.6 习题

1.简单描述一下C语言中的语句。

2.从键盘输入几个字符,再输出该字符自身和它的ASCII代码值。

3.输入大写字母,把它改为小写后输出。

4.已知圆的半径,计算圆面积。

5.输入三个数,求其中的最大值。

第7章 顺序结构与选择结构

C语言程序设计总体上包括两个方面的内容:数据定义和数据操作,数据定义是指程序中的数据描述语句,用来定义一系列数据的类型,完成数据的初始化等;数据操作是指程序中的操作控制语句,用来控制程序的执行过程。一般程序的执行结构包括三种:顺序结构、选择结构和循环结构。前几章我们学习了数据定义方面的有关内容,本章将重点介绍C语言程序设计的数据操作。本章内容如下: 顺序结构程序设计; 选择结构程序设计; 应用举例。

7.1 顺序结构程序设计

顺序结构程序是最简单,最基本的程序设计,它由简单的语句组成,程序的执行是按照程序员书写的顺序进行的,没有分支、转移、循环,且每条语句都将被执行。顺序结构的程序是从上到下依次执行的,其执行流程如图7-1所示。图7-1 顺序结构执行流程【实例7-1】使用putchar()函数显示字符。编写程序如下:#include "stdio.h"#include "string.h" /*添加头文件*/void main(){char a,b,c,d,e; /*定义字符型变量*/a='B'; /*为a赋值*/b=98; /*为b赋值*/c='\101'; /*为c赋值*/d='\\'; /*为d赋值*/e='\''; /*为e赋值*/putchar(a); /*输出a的值*/putchar(b); /*输出b的值*/putchar(c); /*输出c的值*/putchar(d); /*输出d的值*/putchar(e); /*输出e的值*/}

程序的执行结果为:BbA\’

该程序在运行过程中,按照语句的顺序依次执行,逐个输出a、b、c、d、e这5个字符,而且每个语句只执行一次。

该程序的执行顺序如图7-2所示。图7-2 程序的执行顺序图【实例7-2】求三角形的周长和面积。

程序分析:① 输入三条边a、b、c;

     ② 计算周长:l=a+b+c;

     ③ 计算面积:根据海伦公式,半周长h1=(a+b+c)/2;

     三角形面积:s=sqrt(hl*(hl-a)*(hl-b)*(hl-c));

     ④ 输出三角形的面积和周长。

编写程序如下:#include #include /*添加数学函数所需头文件*/void main(){float a,b,c,l,h1,s;printf("请输入能组成三角形的三条边:\n"); /*输出提示文字*/scanf("%f,%f,%f",&a,&b,&c); /*输入a、b、c的值*/printf("a=%f,b=%f,c=%f\n",a,b,c); /*输出a、b、c的值*/l=a+b+c; /*计算三条边的和*/h1=l/2; /*为h赋值*/s=sqrt(h1*(h1-a)*(h1-b)*(h1-c)); /*计算三角形的面积*/printf("三角形的周长和面积分别为:\n"); /*输出面积的值*/printf("l=%4.2f,s=%4.2f",l,s); /*输出l和s的值*/}

程序运行时,输入:3,4,5↙

屏幕上输出的结果为:a=3.000000,b=4.000000,c=5.000000三角形的周长和面积分别为:l=12.00,s=6.00

该程序在执行过程中,首先输出提示信息:“请输入能组成三角形的三条边:”,然后根据要求输入,再输出三条边的大小。

先求三角形周长,再求半周长,利用海伦公式求出三角形面积,最后输出三角形的周长和面积。

程序的执行流程如图7-3所示。图7-3 程序执行流程图【实例7-3】编写程序实现:学习成绩>=90分的同学用A表示,60~89分之间的用B表示,60分以下的用C表示。#includevoid main(){int score;char grade;printf("please input a score\n");scanf("%d",&score);grade=score>=90?'A':(score>=60?'B':'C');printf("%d belongs to %c\n",score,grade);}

程序运行的结果为:please input a score:50↙50 belongs to C

7.2 选择结构程序设计

由于顺序结构程序是顺序执行的,无分支、无转移、无循环,因此它不可能处理复杂的问题,而在数据处理过程中,通常需要根据不同的条件进行判断,然后选择程序进行处理,由此可见,顺序结构无法满足要求,而选择结构就是为了解决这类问题而设定的。

一般而言,C语言中选择语句包括两种:if语句和switch语句。所谓选择语句,就是通过判断条件来选择执行哪一条语句,进而达到编程目的。7.2.1 if语句

if语句又称为条件语句,可以实现多路分支。C语言中,if语句一般格式如下:if(条件1){语句1}else if(条件2){语句2}else if (条件3){语句3}…else if (条件m){语句m}else{语句m+1}

其中,<条件1>,<条件2>,<条件3>…<条件m>表示if语句的条件表达式,用来判断执行哪一条语句。在执行if语句时,先对条件表达式求解,然后根据结果执行指定语句。这里条件表达式可以是逻辑表达式、关系表达式等。

一个判断条件的结果只有两种可能:条件成立与不成立。在许多高级语言中,都用逻辑值“真”表示条件成立;用逻辑值“假”表示条件不成立。在C语言中,没有专门的逻辑值,而是借助于非0值代表“真”,0值代表“假”。只要条件表达式的值为非0,if条件就成立,执行其后面的语句。例如:int y=0;if(a){y=4; /*如果为真,y值为4*/}else{y=5; /*否则,y值为5*/}

这段代码执行后,y的值为4,因为字符“a”的ASCII值为97,if的条件表达式是一个非0值。

语句1,语句2,语句3…语句m+1是if语句的执行语句,可以是一条语句,也可以是复合语句。注意,每条语句的后面都以分号结尾。

在实际应用中,我们很少用到这么多条件。当条件足够多时,我们往往会选择switch语句,这将在下一节中进行详细讲解。1.常用的if语句格式

通常在运用的过程中,if分支语句有几种常用的格式:

格式一:

if语句最简单的格式是没有else,只有if关键字。格式如下:if <条件> 语句

这种if语句的执行过程是,<条件>所给出的表达式为真时,执行语句;当<条件>所给出的表达式为假时,不执行语句,即直接跳过if语句,执行程序下面的语句。程序执行流程如图7-4所示。图7-4 if语句的执行流程图

例如:输出两个数的最大值:if(avoid main( ){float a,b,c,t ;scanf("%f,%f,%f",&a,&b,&c) ; /*输入三个数*/if(a>b) /*使a的值小于b的值*/{t=a;a=b ;b=t;}if(a>c) /*使a的值小于c的值*/{t=a;a=c;c=t;}if(b>c) /*使b的值小于c的值*/{t=b;b=c;c=t;}printf("%5.2f,%5.2f,%5.2f",a,b,c) ; /*将三个数按从小到大的顺序输出/}

格式二:

程序中应用最多的if语句是两路分支,它的基本格式如下:if(条件1){语句1}else{语句2}

该种形式的功能与简单if语句相似,首先判断条件,当<条件1>所给出的表达式为真时,执行语句1,否则执行语句2。程序执行流程如图7-5所示。图7-5 if语句的执行流程图【实例7-5】输入三个数,输出这三个数中的最大数。

程序分析:求最大数,我们可以先两两比较,先比较第一个和第二个数。并把两个数中较大的数存入变量max中。然后再比较max和第三个数的大小,如果第三个数大于max,那就把第三个数存入max中,最后输出max的值。

按照此思想编写的程序如下:#include void main(){float a,b,c,max; /*定义所需变量*/scanf ("%f,%f,%f",&a,&b,&c); /*输入a、b、c的值*//*判断a与b的大小*/if (a>b){max=a;}else{max=b;}/*将a、b中的最大值与c进行比较*/if (max

运行程序,输入:6,2,9

输出结果:最大值为:9.000000

流程图如图7-6所示。图7-6 程序执行流程图2.if语句的嵌套

在if语句中出现的执行语句既可以是一条语句也可以是复合语句,那么在复合语句中可以再次出现if语句吗?当然可以!这就是if语句的嵌套。格式如下:if(条件1){if(条件2){语句1}else{语句2}}else{if(条件3){语句3}else{语句4}}

此种格式最重要的一点就是,注意if与else的配对关系。

else总是与它前面离它最近的未配对的if语句配对,与程序的缩进无关。缩进的作用只是使代码富有层次感,易于他人阅读,与目标代码的生成毫无关系。在设计程序时,为表示清楚程序设计的目的,我们可以适当使用{}来确定配对关系。

一段if与else配对错误的程序如下:int a=0,b=1;if (a==0)if (b==1)printf("Good!\n");elseprintf("Bad!\n");

很多人初看此代码时,以为else是和if (a= =0) 配对的。实际上,如果a不为0,此段代码根本不会执行。只有在a为0,并且b不为1时,才可以执行else语句。由此可见,我们要养成良好的编程风格,确定好各个代码的缩进量。此段代码可以改为:int a=0,b=1;if (a==0){if (b==1){printf("Good!\n");}else{printf("Bad!\n");}}

但是,若我们是希望else和if (a= =0) 配对,则需要写成:int a=0,b=1;if (a==0){if (b==1){printf("Good!\n");}}else{printf("Bad!\n");}

此处运用{}来确定配对关系。所以,以后大家在运用if…else…时,一定要注意它们之间的确切关系。【实例7-6】编写一个程序,输入x的值,按下列公式计算并输出y的值:

程序分析:

输入一个x值; 若x≤1,则y= x; 若1< x <10,则y=2 x–1; 若10≤x,则y=3 x–11。

流程图如图7-7所示。图7-7 程序执行流程图#include void main( ){float x, y ;printf("输入数 x:\n");scanf("%f",&x); /*输入*/if (x<=1){y=x; /*计算*/}else{ if (1

运行程序:输入数 x:3

输出结果:y=5.000000【实例7-7】输入一行字符,分别统计出其中英文字母、空格、数字和其他字符的个数。#include void main(){char c;int letters=0,space=0,digit=0,others=0;/*定义四个整型变量分别用来计算英文字母、空格、数字和其他字符的个数*/printf("please input some characters:\n");while((c=getchar())!='\n'){if(c>='a'&&c<='z'||c>='A'&&c<='Z') /*如果为英文字母变量,letters加1*/letters++;else if(c==' ') /*如果为空格变量,space加1*/space++;else if(c>='0'&&c<='9') /*如果为数字变量,digit加1*/digit++;else /*如果为其他变量,others加1*/others++;}printf("all in all:char=%d space=%d digit=%d others=%d\n",letters,space,digit,others);}

程序的运行结果为:please input some characters:2008 olympic games↙all in all:char=12 space=2 digit=4 others=07.2.2 switch语句

if语句一般用于处理一个或两个分支的选择结构,如果分支较多,就需要使用if语句的嵌套,但嵌套的if语句层数越多,程序越复杂,可读性就越差。C语句提供的switch语句能同时处理多个分支选择结构。其语法格式为:switch(表达式){case 常量1:语句组1case 常量2:语句组2…case 常量n:语句组ndefault:语句组n+1}

switch语句在执行过程中,首先计算表达式的值,当表达式的值与某一个case后面的常量表达式的值相等、相匹配时,就去执行此case后面的语句。如果所有的case中的常量表达式的值都与“表达式”的值都不相匹配,就执行default(默认)后面的语句。图7-8 switch语句执行流程图

这里要说明的是: switch后面括号内的“表达式”可以是任何类型的数据。可以是

整型表达式、字符型表达式,也可以是枚举类型数据。 每个case的常量表达式的值必须互不相同,否则会产生错误的选

择。 各个case和default的出现次序不影响执行的结果。 在执行switch语句时,根据switch后面表达式的值找到匹配的入

口标号,执行完该case语句后,继续执行下一个case语句,不再

进行标号判断。case常量表达式只起到入口标示的作用。【实例7-8】输入考试成绩的等级,打印出百分制分数段(A等:85分以上,B等:70~84,C等:60~69,D等:60分以下)。#include void main( ){char grade; /*定义一个字符型变量*/scanf("%c",&grade); /*输入一个字符型的值*/switch(grade) /*判读输入的值*/{case 'A':printf("85~100\n");case 'B':printf("70~84\n");case 'C':printf("60~69\n");case 'D':printf("<60\n");default:printf("Error\n");}}

程序运行时,当输入:A↙

程序运行的结果为:85~10070~8460~69<60Error

程序运行时,当输入:B↙

程序运行的结果为:70~8460~69<60Error

程序运行时,当输入:C↙

程序运行的结果为:60~69<60Error

程序运行时,当输入:D↙

程序运行的结果为:<60Error

程序运行时,当输入:E↙

程序运行的结果为:Error

从上面的运行结果可以看出,当switch后面圆括号中表达式的值与某个case后面的常量表达式的值相等时,就执行此case后面的语句,然后自动进入下一个case语句继续执行。如果这样执行,会得到一些我们不需要的结果,为了解决这个问题,需要使用break语句,执行一个case后,跳出switch语句。一般形式为:switch(表达式){case 常量1:语句1;break;case 常量2:语句2; break;…case 常量n:语句n; break;default:语句n+1;}

将【实例7-8】修改后为:#include void main( ){char grade; /*定义一个字符型变量*/scanf("%c",&grade); /*输入一个字符型的值*/switch(grade) /*判读输入的值*/{case 'A':printf("85~100\n");case 'B':printf("70~84\n");case 'C':printf("60~69\n");case 'D':printf("<60\n");default:printf("Error\n");}}

程序运行时,当输入:A↙

程序运行的结果为:85~100

程序运行时,当输入:B↙

程序运行的结果为:70~84

程序运行时,当输入:C↙

程序运行的结果为:60~69

程序运行时,当输入:D↙

程序运行的结果为:<60

程序运行时,当输入:E↙

程序运行的结果为:Error

这里需要说明的是,switch-case语句中,default总是放在最后,这里default后不需要break语句,而default也不是必需的,如果switch-case语句中没有default部分,当switch后面圆括号中表达式的值与所有case后面的常量表达式的值都不相等时,则不执行任何一个分支,直接退出switch语句。 在switch-case语句中,多个case可以共用一条执行语句。【实例7-9】输入年号和月号,计算这一年的这个月有多少天。#includevoid main(){int year,month,day;printf("请输入年月:\n");scanf("%d,%d",&year,&month);switch(month){case 1:case 3:case 5:case 7:case 8:case 10:case 12:day=31;break;case 4:case 6:case 9:case 11:day=30;break;case 2:if(year%4==0&&year%100!=0||year%400==0)day=29;elseday=28;break;default:printf("输入的月份是错误的!\n");}printf("year:%d,month:%d,day=%d\n",year,month,day);}

程序运行的结果为:请输入年月:2008,2↙year:2008,month:2,day=29

7.3 应用举例

本章学习结构化程序设计中的顺序结构和选择结构,在讲解的同时给出一些实例,加深读者对本章内容的认识。请读者结合前面所学的知识进行分析。【实例7-10】给定一个不多于5位的正整数,求它是几位数,并逆序打印出各位数字。#includevoid main( ){int a,b,c,d,e,x;scanf("%ld",&x);a=x/10000; /*分解出万位*/b=x%10000/1000; /*分解出千位*/c=x%1000/100; /*分解出百位*/d=x%100/10; /*分解出十位*/e=x%10; /*分解出个位*/if(a!=0)printf("there are 5, %ld %ld %ld %ld %ld\n",e,d,c,b,a);else if(b!=0)printf("there are 4, %ld %ld %ld %ld\n",e,d,c,b);else if(c!=0)printf(" there are 3,%ld %ld %ld\n",e,d,c);else if(d!=0)printf("there are 2, %ld %ld\n",e,d);else if(e!=0)printf(" there are 1,%ld\n",e);}

首先将输入的数按照五位数进行分解,求出万位、千位、百位、十位及个位上面的数字,然后从最高位依次判断该数为几位数,最后逆序将其各位输出。【实例7-11】从键盘上输入若干个学生的成绩,统计并输出最高成绩和最低成绩,当输入负数时结束输入。#include void main(){float x,amax,amin;scanf("%f",&x); /*输入学生的分数*/amax=x; /*最高成绩变量的值等于第一个分数*/amin=x; /*最低成绩变量的值等于第一个分数*/while(x>=0.0) /*成绩不能是负值*/{if(x>amax)amax=x;if(amin>=x)amin=x;scanf("%f",&x);}printf("\namax=%f\namin=%f\n",amax,amin); /*输出最高成绩和最低成绩*/}

首先输入一个学生的成绩,分别将最高成绩和最低成绩赋予该值,再判断该值是否为负,若不为负值则执行循环体,再输入一个学生成绩分别与最高成绩和最低成绩比较,若高于最高成绩则最高成绩等于该值,若低于最低成绩则最低成绩等于该值。【实例7-12】输入某年某月某日,判断这一天是这一年的第几天。#include void main(){int day,month,year,sum,leap;printf("\npleaseinputyear,month,day\n");scanf("%d,%d,%d",&year,&month,&day);switch(month) /*先计算某月以前月份的总天数*/{case1:sum=0;break;case2:sum=31;break;case3:sum=59;break;case4:sum=90;break;case5:sum=120;break;case6:sum=151;break;case7:sum=181;break;case8:sum=212;break;case9:sum=243;break;case10:sum=273;break;case11:sum=304;break;case12:sum=334;break;default:printf("dataerror");break;}sum=sum+day; /*再加上某天的天数*/if(year%400==0||(year%4==0&&year%100!=0)) /*判断是不是闰年*/leap=1;elseleap=0;if(leap==1&&month>2) /*如果是闰年且月份大于2,总天数应该加一天*/sum++;printf("Itisthe%dthday.",sum);}

首先按照常规计算出某月前的月份的天数,以3月5日为例,应该先把前两个月的加起来,然后再加上5天即本年的第几天,然后再考虑特殊情况,闰年且输入月份大于3时需考虑多加一天。【实例7-13】企业发放的奖金根据利润提成。利润(I)低于或等于10万元时,奖金可提10%;利润高于10万元,低于20万元时,低于10万元的部分按10%提成,高于10万元的部分,可提成7.5%;利润在20万到40万之间时,高于20万元的部分,可提成5%;利润在40万到60万之间时高于40万元的部分,可提成3%;利润在60万到100万之间时,高于60万元的部分,可提成1.5%,利润高于100万元时,超过100万元的部分按1%提成,从键盘输入当月利润I,求应发放奖金总数。#include void main(){long int i;double bonus1,bonus2,bonus4,bonus6,bonus10,bonus;scanf("%ld",&i);bonus1=100000*0.1;bonus2=bonus1+100000*0.75;bonus4=bonus2+200000*0.5;bonus6=bonus4+200000*0.3;bonus10=bonus6+400000*0.15;if(i<=100000)bonus=i*0.1;else if(i<=200000)bonus=bonus1+(i-100000)*0.075;else if(i<=400000)bonus=bonus2+(i-200000)*0.05;else if(i<=600000)bonus=bonus4+(i-400000)*0.03;else if(i<=1000000)bonus=bonus6+(i-600000)*0.015;elsebonus=bonus10+(i-1000000)*0.01;printf("bonus=%d",bonus);}

本例也可以考虑使用switch-case语句编写程序,请读者思考完成。

7.4 本章小结

本章主要讲解结构化程序设计语句中的顺序结构、选择结构语句。顺序结构程序是最简单,最基本的程序设计,它由简单的语句组成,程序的执行是按照程序员书写的顺序进行的,没有分支、转移、循环,且每条语句都将被执行。由于顺序结构程序是顺序执行的,无分支、无转移、无循环。

在数据处理过程中,通常需要根据不同的条件进行判断,然后选择程序进行处理,顺序结构无法满足要求,选择结构就是为了解决这类问题而设定的。它可以根据条件进行判断,用于解决复杂的问题。

7.5 习题

1.顺序结构与选择结构有什么不同?

2.选择语句分为哪几种?

3.给定一个不多于7位的正整数,求它是几位数,并逆序打印出各位数字。

4.输入某年某月某日,判断这一天是这一年第几个星期中的第几天。

第8章 循环结构程序设计

许多问题都会遇到规律性的重复操作。例如,求累加和问题、求有一定规律的问题和一些迭代等问题都会用到循环结构。循环结构是结构化程序设计的基本结构之一,它与顺序结构、选择结构共同构成了各种复杂程序的基本构造单元。本章重点讲解三种循环语句:while语句、do…while语句和for语句。本章内容如下: while语句; do…while语句; for语句; 总结应用。

8.1 while语句

while语句的一般形式如下(如图8-1、图8-2所示):图8-1 while语句执行流程图图8-2 while语句执行N-S图while(表达式){循环语句}

对于while语句说明以下几点: 在C语言中while语句也被称为当型循环语句,它的执行特点是:

先判断,后执行。 while语句的执行过程:(1)先判断循环条件是否成立,即判断表达式是否为0。(2)当表达式为0时,循环不执行,跳到下一个语句。当表达式不为0时,执行循环语句,然后再转到(1)继续执行。【实例8-1】计算100以内正整数的和。

程序分析:

求100以内正整数的和,就是求表达式1+2+3+…+100结果,使用while循环语句,控制循环的条件是变量i的值不大于100,只要i不大于100,就继续执行,每循环一次i的值就增加1,当i>100时,循环条件不满足,循环结束,输出结果。

编写程序如下:#includevoid main(){int i,sum=0;i=1;while(i<=100) /*while循环语句*/{sum+=i; /*依次加入*/i++; /*循环一次i的值就增加1*/}printf("1+2+3+…+100=%d\n",sum); /*输出结果*/}

程序运行的结果为:1+2+3+…+100=5050

在本例中,程序执行到while语句时,首先判断while语句中的条件是否满足,即i是否小于或等于100,如果条件为真,则执行循环体,否则结束循环。【实例8-2】编一个程序,求费波那契(Fibonacci)序列:1,1,2,3,5,8,……请输出前20项。

序列满足关系式:F=F+Fnn-1n-2

程序分析:

① 通过观察费波那契(Fibonacci)序列,F=1,F=1,以后各2项都等于前两项之和,序列满足关系式:F=F+F。nn-1n-2

② 使用程序要求输出前20项之和,前两项已知,需要从第三项开始,因此,使用while循环,计数i从3开始,判断条件是i<=20。

③ while循环体中,求出t=F+F的和为费波那契(Fibonacci)序12列的第三项,然后将第二项和第三项作为第四项的前两项,计数i加1,依此类推。

程序设计如下:#include void main( ){int i, t, f1=1, f2=1 ;printf("%d %d ", f1,f2);while(i<=20){t=f1+f2;printf("%d ", t); /* 求出新的数 */if(i%5==0) /*每五项为一列输出*/printf("\n");f1=f2;f2=t; /* 对f1和f2更新 */i++;}}

程序运行的结果如图8-3所示。图8-3 程序运行的结果【实例8-3】某厂今年产量为100万元,假定该厂的产值每年增长10%,问几年后产值可以翻一番?

程序分析:

现在产量用变量sum存储,则一年以后的产量为:sum*(1+10/100)—>sum,产值小于200。再计算两年后产值为:sum*(1+10/100)—>sum,产值仍小于200。再计算三年后的产值……直到产值超过200万为止。

编写程序如下:#include void main(){int year=0,sum=100; /*定义所需变量并赋值*//*while循环*/while (sum<200) /*如果sum的值小于200*/{sum=sum*(1+10.0/100);year+=1;}printf("%d\n",year); /*输出计算的年份*/}

程序运行的结果为:8 while语句是可以循环嵌套使用的,即在while语句循环内部嵌套

一个或多个while循环语句。形式为:while(){…while(){…}}【实例8-4】使用while循环嵌套编写九九乘法表。#include void main(){int x = 1, y = 1;while (x <= 9){y = 1;while (y <= x){printf("%d*%d=%d\t", y , x, x * y);y++;}printf("\n");x++;}}

程序运行的结果如图8-4所示。图8-4 程序运行结果

8.2 do…while语句

do…while语句是另一种循环结构,它和while语句的不同点在于,do…while语句先执行循环,然后判断循环条件是否成立。其一般形式如下(如图8-5、图8-6所示):图8-5 do…while语句流程图图8-6 do…while语句N-S图do循环语句while(表达式);

对于do…while语句做以下几点说明: 在C语言中do…while语句被称为直到型循环,它的执行特点是:

先执行,后判断。 do…while语句执行过程:(1)先执行一次循环语句,然后判断

表达式的值。(2)若表达式的值为非0,则返回(1);如果表达

式的值为0,则直接退出循环语句,执行下一条语句。【实例8-5】从键盘输入一个正整数n,计算该数的各位数之和并输出。例如,输入数是4569,则计算4+5+6+9=24并输出。

程序分析:(1)求出输入正整数各位数字之和,首先要求出各位上的数字。(2)使用do…while语句进行循环操作,循环体为:

①n对10求余,得出个位上的数字。如:4569%10=9

②n对10求商,使用n缩小一位,然后求高一位上面的数字,再赋值给n。

③ 每求出一个数位上的数字,使用s求和。

如:4569/10=456,n=456。(3)当n>0,再次执行循环体,直到n<0。

程序设计如下:#include void main( ){int n, s=0;printf("输入一个正整数:");scanf("%d",&n);do{s+=n%10;

试读结束[说明:试读内容隐藏了图片]

下载完整电子书


相关推荐

最新文章


© 2020 txtepub下载