您正在查看: c++ 分类下的文章

第4章_数组



出处:C++大学教程


教学目标
●介绍数组数据结构
●介绍用数组存放、排序与查找数值清单与表格
●介绍如何声明数组、初始化数组和引用数组的各个元素
●将数组传递到函数中
●介绍基本排序方法
●声明和操作多下标数组

4.1简介
本章是介绍数据结构的重要课题。数组(array)数据结构由相同类型的相关数据项组成。第6章介绍结构(structure)和类(class),两者都可以带有不同类型的相关数据项。数组和结构是静态项目.在整个程序执行期间保持相同长度(当然,也可以用自动存储类,在每次进入和离开定义的块时生成和删除)。第15章介绍链表、队列、堆栈、树之类的动态数据结构如何在程序执行期间改变长度。本章介绍的数组是c语言中的指针数组(第5章将介绍指针)。第8章“运算符重载”和本书末尾的"标准模板库(STL)"一章将介绍用面向对象编程技术将数组实现为完全成熟的对象,这些基于对象的数组比第4章介绍的类c语言的指针数组更安全、更灵活。

4.2 数组
数组是具有相同名称和相同类型的一组连续内存地址。要引用数组中的特定位置或元素,就要指定数组中的特定位置或元素的位置号(positionnumber)。
图4.1显示了整型数组c。这个数组包含12个元素。可以用数组名加上方括号(1))中该元素的位置号引用该元素。数组中的第一个元素称为第0个元素(zerothelemem)。这样,c数组中的第一个元素为c[

第2章_控制结构

C++大学教程(第2章控制结构)


第2章 控制结构


教学目标

●了解基本问题的解决方法
●通过自上而下、逐步完善的过程开发算法
●用if、if/else和switch选择结构选择操作
●用while、do/while和for重复结构重复执行程序语句
●了解计数器控制重复与标记控制重复
●使用自增、自减、赋值和逻辑运算符
●使用break和continue此程序控制语句

2.1 简介

编写解决特定问题的程序之前,首先要彻底了解问题并认真计划解决问题的方法。编写程序时,还要了解可用的基本组件和采用实践证明的程序结构原则。本章将讨论结构化编程的理论和原理的所有问题。这里介绍的技术适用于大多数高级语言.包括C++。第6章在介绍C++面向对象编程时,将会介绍如何用第2章介绍的控制结构帮助建立和操作对象。

2.2 算法
任何计算问题都可以通过按特定顺序执行一系列操作而完成。解决问题的过程(procedure)称为算法(algorithm),包括:
1.执行的操作(action)
2.执行操作的顺序(order)
下例演示正确指定执行操作的顺序是多么重要:
考虑每个人早晨起床到上班的“朝阳算法”:(1)起床,(2)脱睡衣,(3)洗澡,(4)穿衣,(5)吃早饭,(6)搭车上班。
总裁可以按这个顺序,从容不迫地来到办公室。假设把顺序稍作调换:(1)起床,(2)脱睡衣,(3)穿衣,(4)洗澡,(5)吃早饭.(6)搭车上班。
如果这样,总裁就得带着肥皂水来上班。指定计算机程序执行语句的顺序称为程序控制(programcontrol),本章介绍C++程序的控制功能。

2.3伪代码
伪代码(pseudocode)是人为的非正式语言,帮助程序员开发算法。这里介绍的伪代码在开发的算法转换为结构化C++程序时特别有用。伪代码类似于日常英语,方便而且容易掌握,但不是实际计算机编程语言。伪代码程序并不在计算机上实际执行,而是帮助程序员先“构思”程序.再用C++之类的实际计算机编程语言编写。本章介绍几个如何在开发结构化C++程序时有效利用伪代码的例子。
我们介绍的伪代码完全由字符构成,程序员可以用一个编辑器程序方便地输入伪代码程序,计算机可以在需要时显示伪代码程序。认真构思的伪代码程序可以方便地变为对应的C++程序。很多情况下,只要将伪代码语句转换成对应的C++语句即可。
伪代码只包含执行语句,将伪代码程序变为对应的C++程序时,这些语句可以运行。声明语句不是执行语句。例如,下列声明:
inti;
只是告诉编译器,变量i的类型是整型,指示编译器在内存中为这个变量保留内存空间。但这个声明并在执行程序时不做任何操作(如输入、输出或计算)。有些程序员在伪代码程序开头列出变量及其简要说明。

2.4 控制结构
通常,程序中的语句按编写的顺序一条一条地执行,称为顺序执行(sequentialexecution)。程序员可以用稍后要介绍的不同C++语句指定下一个执行的语句不是紧邻其后的语句,这种技术称为控制转移(transfer ofcontrol)。
20世纪60年代,人们发现,软件开发小组遇到的许多困难都是由于控制转移造成的。goto语句使程序员可以在程序中任意指定控制转移目标,因此人们提出结构化编程就是为了清除goto语句。
Bohm和JMoP5n1的研究表明,不用goto语句也能编写程序。困难在于程序员要养成不用goto语句的习惯。直到20世纪70年代,程序员才开始认真考虑结构化编程,结果使软件开发小组的开发时间缩短、系统能够及时交付运行并在颅算之内完成软件项目。这些成功的关键是.结构化编程更清晰、更易调试与修改并且不容易出错。
BohM和J“jecopini的研究表明,所有程序都可以只用三种控制结构(control structure)即顺序结构(sequencestructure)、选择结构(selection structure)和重复结构(repetitionstructure)。顺序结构是C++内置的,除非另外指定,计算机总是按编写的顺序一条一条地执行。图2.1的流程图(flowchart)演示了典型的顺序结构.按顺序进行两次计算。
流程图是算法或部分算法的图形表示。流程图用一些专用符号绘制,如长方形、菱形、椭圆和小圆,这些符号用箭头连接,称为流程。
和伪代码一样,流程图也用于开发和表示算法,但伪代码更受欢迎。流程图能清楚地表示控制结构如何操作.本书用流程图表示控制结构如何操作。
考虑图2.1所示的流程图。我们用矩形框(或称为执行框)表示各种操作,包括计算、输入和输出操作。图中的流程表示进行操作的顺序,首先将grade加进total,然后将counter加1。C++允许顺序结构中有多个操作,稍后可以看出,可以放一个操作的地方,也就可以放几个顺序操作。
绘制表示完整算法的流程图时,椭圆框加上其中的“Begin”(开始)字样表示流程图开始,椭圆框加上其中的“End”(结束)字样表示流程图结束。只画部分算法时(如图2.1),省略椭圆框,只用小圆框,也称接头框。



最重要的流程图符号是菱形框,也称为判断框,表示要进行判断。下一节将介绍菱形框。
C++提供三种选择结构,本章将介绍这三种选择结构。if选择结构在条件为true时执行一个操作,在条件为false时跳过这个操作。if/else选择结构在条件为true时执行一个操作,在条件为false时执行另一个操作。swutch选择结构根据表达式取值不同而选择不同操作。
if选择结构称为单项选择结构(single—selection,structure),选择或忽略一个操作。if/else选择结构称为双项选择结构(double-selectionstructure),在两个不同操作中选择。switch选择结构称为多项选择结构(multiple-selectionstructure),在多个不同操作中选择。
C++提供三种重复结构while、do/while和for。if、else、switch、while、do和for等都是C++关键字(keyword)。这些关键字是该语言保留的,用于实现如C++控制结构等不同特性。关键字不能作为变量名等一些标识符。图2.2显示了完整的C++关键字列表。
-----------------------------------------------------------------------------------------------


----------------------------------------------------------------------------------------------

常见编程错误 2.1
用关键字作为标识符属于语法错误。

C++只有七种控制结构:顺序结构、三种选择结构和三种重复结构。每个C++程序都是根据程序所需的算法组合这七种控制结构。从图2.1中的顺序结构可以看出,每个控制结构的流程图使用两个小圆框,一个在控制结构入口点,一个在控制结构出口点。这种单入/单出控制结构(single-entry/single-exitcontrolstructure)使程序容易建立,只要将一个控制结构的出口与另一个控制结构的入口连接,即可组成程序。这点很像小孩子堆积木,因此称为控制结构堆栈(control-structurestacking),还有另一种控制结构连接方法,称为控制结构嵌套(control-structure nesing)。软件工程视点 2.1
任何C++程序可以用这七种控制结构(顺序、if、if/else、switch、while、do/while和for并用两种方式(控制结构堆栈和控制结构嵌套)组合而成。

2.5if选择结构 选择结构在不同操作之间选择。例如,假设考试成绩60分算及格,则下列伪代码:
if student'sgrade is greater than or equal to60
print"Passed"
确定“学生成绩大于或等于60分”是true或false,如果是true,则该生及格,打印“Passed”字样,并顺序“执行”下一个伪代码语句(记住,伪代码不是真正的编程语言)。如果条件为false,则忽略打印语句,并顺序“执行”下一个伪代码语句。注意这个选择结构第二行的缩排,这种缩排是可选的,但值得提倡,因为它能体现结构化程序的内部结构。将伪代码变成C++代码时,C++编译器忽略空格、制表符、换行符等用于缩排和垂直分隔的空白字符。
编程技巧 2.1
在整个程序中坚持用合理缩排规则能大大提高程序可读性。建议用固定制表长度即1/4英寸或三个空格的缩排量。 上述伪代码的if语句可以写成如下C++语句:
if(grade>=60)
cout<<"Passed";
cout<<"Passed";
注意C++代码与伪代码密切对应,这是伪代码的一个属性,使得其成为有用的程序开发工具
编程技巧 2.2
伪代码常用于程存设计期间“构思”程序,然后再将伪代码程序转换为C++程序。

图2.3的流程图演示了单项选择if结构。这个流程图包含流程图中最重要的菱形框、也称判断框,表示要进行判断。判断框包含一个表达式(如条件),可取true或false值。判断框产生两条流程,一条指向表达式为true时的走向,一条指向表达式为false时的走向。第1章曾介绍过,可以根据包含关系或相等运算待的条件作出判断。实际上,可以针对任何表达式作出判断,如果表达式求值为0,则当作false,如果表达式求值非0,则当作true。C++草案标准提供bool数据类型,表示true和false。关键字true和false表示bool数据类型的值。
注意,if结构也是单入/单出结构。稍后将会介绍,其余控制结构的流程图(除了小圆框和流程之外)也只能包含表示所要操作的矩形框和表示所要判断的菱形框。这是我们强调的操作/判断编程模型(action/decisionmodel of progamming)。
可以想像有七个框,各包含七种控制结构中的一种控制结构,这些控制结构是空的,矩形框和菱形框中什么也没有。程序员的任务就是根据算法需要用堆栈和嵌套两种方法组合这几种控制结构,然后在这些框中填入算法所要的操作和判断,从而生成程序。下面介绍编写操作和判断的各种方式。



2.6if/else选择结构
if选择结构只在条件为true时采取操作,条件为false时则忽略这个操作。利用if/else选择结构则可以在条件为true时和条件为false时采取不同操作。例如,下列伪代码:
if student's grade is greater than or equal to60
print"Passed"
else
print"Failed"
在学生成绩大于或等于60时打印“Passed”,否则打印"Failed"。打印之后,都“执行”下一条伪代码语句。注意else的语句体也缩排。

编程技巧 2.3
if/eIse选择结构的两个语句体都缩排。 选择的缩徘规则应当在整个程序中认真贯彻执行。不按统一缩排规则编写的程序很难阅读。编程技巧 2.4
如果有多层缩排,则每一层应缩排相同的空间量。 上述伪代码if/else结构可以写成如下的C++代码:
if(grade>=60)
cout<<"Passed";
else
cout<<"Failed";
图2.4的流程图很好地演示了if/else结构的控制流程。注意,这个流程图(除了小圆框和流程之外)也只能包含表示所要操作的矩形框和表示所要判断的菱形框。这里我们继续强调操作/判断模型计算,假设框中包含建立C++程序所需的空白双项选择结构。程序员的任务就是根据算法需要用堆栈和嵌套两种方法组合各种控制结构,然后在这些框中填人算法所要的操作和判断,从而生成程序。
C++提供条件运算符(?:),与if/else结构密切相关。条件运算符是C++中惟一的三元运算符(thrnaryoperator),即取三个操作数的运算符。操作数和条件运算符一起形成条件表达式(conditionalexpression)。第一个操作数是条件,第二个操作数是条件为true时整个条件表达式的值.第三个操作数是条件为false时整个条件表达式的值。例如,下列输出语句:
cout<<(grade>=60?"Passed":"Failed");
包含的条件表达式在grade=60取值为true时,求值为字符串“Passed”;在grade>=60取值为false时,求值为字符串"Failed"。这样,带条件表达式的语句实际上与上述if/else语句相同。可以看出,条件运算符的优先级较低,因此上述表达式中的话号是必需的。



条件表达式酌值也可以是要执行的操作。例如.下列条件表达式:
grade >=60?cout<<"Passed":cout<<"Failed";
表示如果grade大于或等于60,则执行cout<<"Passed",否则执行cout<<"Failed"。这与前面的if/else结构也是相似的。条件运算符可以在一些无法使用if/else语句的情况中使用。
嵌套if/else结构(nested if/elsestructure)测试多个选择,将一个if/else选择放在另一个if/else选择中。例如,下列伪代码语句在考试成绩大于或等于90分时打印A.在80到89分之间时打印B,在70到79分之间时打印C,在60到69分之间时打印D,否则打印F。

if studen's grade is greater than orequal to 90
print"A"
else
If student's grade isgreater than or equal to80
print"B"
else
If student's grade is greater than or equal to70
print "C"
else
If student's grade isgreater than or equal to60
print "D"
else
print"F"
这个伪代码对应下列C++代码:
if(grade>=90)
cout<<"A";
else
if(grade>=80)
cout<<"B";
else
if(grade>=70)
cout<<"C";
else
if(grade>=60)
cout<<"D";
else
cout<<"F";
如果考试成绩大于或等于90分,则前4个条件都为true,但只执行第一个测试之后的cout语句。执行这个cout语句之后,跳过外层if/else语句的else部分。许多C++程序员喜欢将上述if结构写成:

if(grade>=90)
cout<<"A";
elseif(grade>=80)
cout<<"B";
elseif(grade>=70)
cout<<"C";
elseif(grade>=60)
cout<<"D";
else
cout<<"F";
两者形式是等价的。后者更常用,可以避免深层缩排便代码移到右端。深层缩排会使一行的空间太小,不长的行也要断行,从而影响可读性。
编程提示2.1
嵌套if/else结构比一系列单项选择if结构运行速度快得多,因为它能在满足其中一个条件之后即退出。

性能提示2.2
在嵌套if/else结构中,测试条件中true可能性较大的应放在嵌套if/else结构开头.从而使嵌套if/else结构运行更快,比测试不常发生的情况能更早退出。
if选择结构体中只能有一条语句。要在if选择结构体中包括多条语句,就要把这些语句放在花括号({})中。放在花括号中的一组语句称为复合语句(compund statement)。

软件工程视点2.2
复合语句可以放在程序中出现单句语句的任何地方。 下例在if/else结构的else部分包括复合语句:
if(grade>=60)
cout<<"Passed.\n";
else{
cout<<"Failed.\n";
cout<<"You must take this course again.\n";
}
如果grade小于60,则程序执行else程序体中的两条语句并打印:
Failed.
You must take this courseagain.
注意else从句中的两条语句放在花括号中。这些花括号很重要,如果没有这些花括号,则下列语句:
cout<<"You must take this coursagain.\n";
在if语句else部分之外,不管成绩是否小于60都执行。

常见编程错误2.2
忽略复合语句中的一个或两个花括号可能在程序中生成语法错误或逻辑错误。

编程技巧2.5
总是在if/else结构(和任何控制结构)中放上花括号,可以避免不慎疏忽,特别是后面要在if或else语句中增加语句时。错误(fatallogicerror)使程序失败和提前终止,而非致命逻辑错误(nonfatal logicerror)则让程序继续执行,只是产生错误结果。

软件工程视点2.3
复合语句可以放在程序中出现单句语句的任何地方,也可以根本不放语句,即放上空语句。空语句就是在正常语句出现的地方放一个分号(;)。

常见编程错误2.3
在if结构条件后面放上分号会造成单项选择if结构的逻辑错误和双项选择if结构的语法错误(如果if部分包含实际语句体)。

编程技巧2.6
有些程序员喜欢在花括号中输入各个语句之前输入复合语句的开始花括号和结束花括号。这样可以避免丢失一个或两个花括号。

本节介绍了复合语句的符号。复合语句可以包含声明(例如,和main程序体中一样),如果这,则这个复合语句称为块(block)。块中的声明通常放在块中任何操作语句之前,但也可以和操作语句相混和。第3章将介绍块的用法,在此之前,读者应避免使用块(除了作为main程序体)。

2.7while重复结构
重复结构(repetitionstrucure)使程序 员可以指定一定条件下可以重复的操作。下列伪代码语句:
While thereare more items on my shopping list
Purchase nextitem and cross it off my list
描述购物过程中发生的重复。条件"there are more ltems onmy shopping list"(购物清单中还有更多
项目)可真可假。如果条件为true.则执行操作"Purchase next itemand cross it off mylist"(购买下
一个项目并将其从清单中划去)。如果条件仍然为true,则这个操作重复执行。while重复结构中的
语句构成while的结构体,该结构体可以是单句或复合句。最终,条件会变为false(购买清单中最
后一个项目并将其从清单中划去时),这时重复终止,执行重复结构之后的第一条伪代码语句。

常见编程错误2.4
如果不在while结构中提供最终导致while条件变为false的操作,则合造成无限循环(infiniteloop)错误,重复结构永不终止。
常见编程错误2.5
将关键字while的拼写变为“While”是个语法错误,因为C++是区分大小写的语言。while、if和else等所有C++保留关键字只能包含小写字母。

作为实际while的例子,假设程序要寻找2的第一个大于1000的指数值。假设整数变量prod-
uct初始化为2,执行下列while重复结构之后,product即会包含所要值:
int product = 2;
while ( product <= 1000)
product = 2 *product;
图2.5的流程图演示了对应于上述while重复结构的while结构控制流程。注意,流程图(除了
小圆框和流程之外)也只能包含表示所需操作的矩形框和表示所需判断的菱形框。这是我们强调的
操作/判断编程模型。程序员的任务就是根据算法需要用堆栈和嵌套两种方法组合其他几种控制结
构,然后在这些框中填入算法所要的操作和判断,从而生成程序。流程图中清楚地显示了重复。流
程从矩形出发,回到判断框中测试,直到判断为false。然后退出while结构,控制转入程序中下一
条语句。


进入while结构时,product的值为2。变量product重复乘以2,连续取值4、8、16、32、64、
128、256、512和1024。当product变为1024时,while结构条件product<=1000变为false,因此终
止重复,product的最后值为1024。程序继续执行while后面的下一条语句。

2.8构造算法:实例研究1(计数器控制重复)
要演示如何开发算法,我们要解决几个全班平均成绩的问题。考虑下列问题:
班里有10个学生参加测验,可以提供考试成绩(0到100的整数值),以确定全班平均成绩。
全班平均成绩等于全班成绩总和除以班里人数。计算机上解决这个问题的算法是辅人每人的成绩,进行平均计算,然后打印结果。
下面用伪代码列出要执行的操作,指定这些操作执行的顺序。我们用计数器控制重复(counter-conttrolledrepetition)一次一个地输人每人的成绩。这种方法用计数器(counter)变量控制一组语句执行的次数。本例中,计数器超过10时,停止重复。本节介绍伪代码算法(如图2.6)和对应程序(如图2.7)。下节介绍如何开发这个伪代码算法。计数器控制重复通常称为确定重复(definiterepetition),因为循环执行之前,已知重复次数。
注意算法中引用了总数(total)和计数器。总数变量用于累计一系列数值的和。计数器变量用于计数,这里计算输人的成绩数。存放总数的变量通常应先初始化为0之后再在程序中使用,否则总和会包括总数的内存地址中存放的原有数值。
Set total to zero
Set grade counterto one
While grade counter is less thanor equal toten
Input the nextgrade
Add the grade i.to thetotal
Add one to the grade counter
Set theclass average to the total divided byten
Print the classaverage

图2.6 用计数器控制重复解决全班平均成绩问题的伪代码算法

1 // Fig. 2.7:fig0207.cpp
2 // Class average program withcounter-controlled repetition
3 #include<iostream.h>
4
5 intmain()
6{
7 inttotal, // sum ofgrades
8 gradeCounter, //number of grades entered
9grade, // onegrade
10average; // average ofgrades
11
12 //initialization phase
13 total =0;// clear total
14 gradeCounter =1;// prepare to loop
15
16 // processing phase
17while( gradeCounter <= 10 ){ // loop 10times
18cout << "Enter grade:"; // prompt forinput
19cin >>grade;// input grade
20 total = total +grade;// add grade to total
21 gradeCounter = gradeCounter +1; // increment counter
22 }
23
24 // termination phase
25 average - total /10;// integer division
26 cout<< "Class average is "<< average <<endl;
27
28return 0; // indicate program endedsuccessfully
29 }
输出结果:
Entergrade: 98
Enter grade: 76
Enter grade: 71
Enter grade:87
Enter grade: 83
Entergrade: 90
Enter grade: 57
Enter grade: 79
Enter grade:82
Enter grade: 94
Classaverage is81

图2.7 用计数器控制重复解决全班平均成绩问题的C++程序和示例输出

根据使用情况,计数器变量通常应先初始化为0或1(下面会分别举例说明)。未初始化变量会包含垃圾值“garbage”value),也称为未定义值(undefinedvalue),为该变量保存内存地址中最后存放的值。

常见编程错误2.6
如果不初始化计数器和总和变量,则程序的结果可能不正确.这是一种逻辑错误。编程技巧2.7
一定要初始化计数器和总和变量。编程技巧2.8
每个变量在单独一行中声明。 注意程序中的平均计算产生一个整数结果。实际上,本例中的成绩总和是817,除以10时应得到81.7,是个带小数点的数,下节将介绍如何处理这种值(称为浮点数)。

常见编程错误2.7
在计数器控制循环中,由于循环计数器(每人循环加1时)比最大合法值多1(例如,从1算到10时为11).因此在循环之后用计数器值进行计算通常会出现差1的错误。
图2.7中,如果第21行用gradeCounter而不是10进行计算,则这个程序的输出显示数值74。

2.9构造算法与自上而下逐步完善:实例研究2(标记控制重复)
下面将全班平均成绩问题一般化,考虑如下问题:
开发一个计算全班平均成绩的程序,在每次程序运行时处理任意个成绩数。
在第一个全班平均成绩例子中,成绩个数(10)是事先预置的。而本例中,则不知道要输入多少个成绩,程序要处理任意个成绩数。程序怎么确定何时停止输入成绩呢?何时计算和打印全班平均成绩呢?
一种办法是用一个特殊值作为标记值(sentinelvalue),也称信号值(signalvalue)、哑值(dummyvalue)或标志值(flag value),表示数据输入结束(“end of dataentry”)用户输入成绩,直到输入所有合法成绩。然后用户输入一个标记值,表示最后一个成绩已经输入。标记控制重复(sentinel-controlledrepetition)也称为不确定重复(indefiniterepetition),因为执行循环之前无法事先知道重复次数。
显然,标记值不能与可接受的输入值混淆起来。由于考试成绩通常是非负整数,因此可以用-1作标记值。这样,全班平均成绩程序可以处理95、96、75、74、89和-l之类的输人流。程序计算并打印成绩95、96、75、74和89的全班平均成绩(不计入-1,因为它是标记值)。

常见编程错误2.8
将选择的标记值与可接受的输入值混淆时会造成逻辑错误。
我们用自上而下逐步完善(top-down,stepwiserefinement)的方法开发计算全班平均成绩的程序,这是开发结构化程序的重要方法。我们首先生成上层伪代码表示:
Determine the class avcraqe for thequiz
上层伪代码只是一个语句,表示程序的总体功能。这样.上层等于是程序的完整表达式。但上层通常无法提供编写C++程序所需的足够细节。因此要开始完善过程。我们将上层伪代码分解为一系列的小任务,按其需要完成的顺序列出。这个结果就是下列第一步完善(first,refinement):
Initialize variables
Input,sum,and count the quizgrades
Calculate and print the classaverage
这里只用了顺序结构,所有步骤按顺序逐步执行。

软件工程视点2.4
上层伪代码及每一步完善都是算法的完整定义,只是详细程度不同而已。
软件工程视点2.5
许多程序可以在逻辑上分为三个阶段:初初化阶段将程序变量初始化,处理阶段输入数据值和相应调整程序变量,结束阶段计算和打印最后结果。
上述“软件工程视点”通常是自上而下过程第一步完善的全部工作。要进行下一步完善(即第二步完善,secondrefinement),我们要指定特定变量,要取得数字的动态和以及计算机处理的数值个数,用一个变量接收每个输入的成绩值,一个变量保存计算平均值。下列伪代码语句:
Initialize variables
可以细化成: Initialize total tozero
Initialize counter tozero
注意,只有total和counter变量要先初始化再使用,average和grade变量(分别计算平均值和用户输入)不需要初始化.因为它们的值会在计算或输入时重定义。
下列伪代码语句:
Input, sum, and count the quizgrades
需要用重复结构(即循环)连续输入每个成绩。由于我们事先不知道要处理多少个成绩,因此使用标记控制重复。用户一次一项地输入合法成绩。输入最后一个合法成绩后,用户输人标记值。程序在每个成绩输入之后测试其是否为标记值.如果用户输入标记值,则顺序循环终止。上述伪代码语句的第二步完善如下:
Input the first grade (possibly the sentinel)
Whilethe user has not as yet entered the sentinel
Add thisgrade into the running total
Add one to the gradecounter
Input the next grade (possibly thesentinel)
注意,在这个伪代码中,我们没有在while结构体中使用花括号,只是在while下面将这些语句缩排表示它们属于while。伪代码只是非正式的程序开发辅助工具。
下列伪代码语句可以完善如下:
If the counter is not equal tozero
Set the average to the total divided bythe counter
Print theaverage
else
Print "Nogrades wereentered"
注意我们这里要测试除数为0的可能性,这是个致命逻辑错误,如果没有发现,则会使程序失败(通常称为爆炸或崩溃)。图2.8显示了全班平均成绩问题第二步完善的完整伪代码语句。
常见编程错误2.9
除数为0是个致命逻辑错误。
编程技巧2.9
进行除法时,要测试除数为0的可能性,并在程序中进行相应处理(如打印一个错误消息).而不是让致
命逻辑错误发生。
图2.6和图2.8的伪代码中增加了一些空行,使伪代码更易读。空行将程序分成不同阶段。
图2.8所示的伪代码算法解决更一般的全班平均成绩问题,这个算法只进行了第二步完善,还
需要进一步完善。
Initialize total to zero
Initialize counter tozero

Input the first grade (possibly thesentinel)
While the user has not as yet entered thesentinel
Add this grade into therunning total
Add one to the gradecounter
Input the next grade(possibly the sentinel)

if the counter is notrqual to zero
Set the average tothe total divided by the counter
Print the average
else
Print "No grades wereentered"

图2.8 用标记符控制重复解决全班平均成绩问题的伪代码算法

软件工程视点2.6
伪代码算法的细节足以将伪代码变为C++代码时,程序员即可停止自上而下逐步完善的过程,然后就可
方便地实现C++程序。
图2.9显示了C++程序和示例执行结果。尽管只输入整数成绩,但结果仍然可能产生带小数点
的平均成绩,即实数。int类型无法表示实数,程序中引入float数据类型处理带小数点的数(也称
为浮点数,floatingpointnumber),并引入特殊的强制类型转换运算符(castoperator)处理平均值计
算。这些特性将在程序之后详细介绍。
1 // Fig. 2.9:fig02_09.cpp
2 // Class average program withsentinel-controlled repetition.
3 #include<iostream.h>
4 #include<iomanip.h>
5
6 intmain()
7 {
8 int total, // sum ofgrades
9 gradeCounter, //number of grades entered
10grade; // one grade
11float average; // number with decimal point foraverage
12
13 //initialization phase
14 total =0;
15 gradeCounter = 0;
16
17 // processing phase
18cout << "Enter grade, -1 to end: ";
19 cin>> grade;
2O
21 while (grade !=-1 ) {
22 total = total +grade;
23 gradeCounter = gradeCounter +1;
24 cout << "Enter grade, -1 toend: ";
25 cin >>grade;
26 }
27
28 // termination phase
29if ( gradeCounter != 0 } {
30 average -static_cast< float >( total ) / gradeCounter;
31 cout << "Class average is "<< setprecision{ 2)
32 << setiosflags(ios::fixed | ios::showpoint )
33 << average <<endl;
34 }
35else
36 cout << "NO grades wereentered" << endl;
37
38 return 0; //indicate program ended successfully
39}

输出结果:
Enter grade, -1 toend: 75
Enter grade, -1 to end:94
Enter grade, -1 to end: 97
Enter grade,-1 to end: 88
Enter grade, -1 to end:70
Enter grade, -1 to end: 64
Enter grade, -1 to end: 83
Enter grade, -1 to end:89
Enter grade, -1 to end: -1
Class average is82.50

图2.9 用标记符控制重复解决全班平均成绩问题的C++程序和示例执行结果

注意图2.9中while循环中的复合语句。如果没有花括号,则循环体中的最后三条语句会放到循环以外,使计算机错误地理解如下代码:
while { grade ! = -1 )
total -total + grade;
gradeCounter = gradeCounter +1;
cout << "Enter grade, -1 toend:";
cin >>grade;

如果用户输入的第一个成绩不是-l,则会造成无限循环。
注意下列语句:
cin >>grade;

前面用一个输出语句提示用户输入。

编程技巧2.10
提示用户进行每个键盘输入。提示应表示输入形式和任何特殊输入值(如用户终止循环时输入的标记值)。
编程技巧2.11
在标记控制循环中,提示请求输入数据项目时应显式指定标记值是什么值。
平均值并不一定总是整数值,而常常是包含小数的值,如7.2或-93.5。这些值称为浮点数,用数据类型float表示。变量average声明为数据类型float,以获得计算机结果中的小数。但total/gradeCounter的计算结果是整数,因为total和gradeCounter都是整数变量。两个整数相除是整除(integerdivision),小数部分丢失(即截尾,truncated)。由于先要进行计算,因此小数部分在将结果赋给average之前已经丢失。要用整数值进行浮点数计算,就要先生成用于计算的临时浮点数值。
C++提供了一元强制类型转换运算符(unarycast operator)。下列语句:
average = static cast< float>(total) /gradeCounter;
包括一元强制类型转换运算符static_cast<float>(),生成用于计算的临时浮点数值(total)。这样使用强制类型转换运算符称为显式类型转换(explicitconversion)。total中存放的值还是整数,而计算时则用浮点数值(total的临时float版本)除以整数gradcCounter。
c++编译器只能对操作数的数据类型一致的表达式求值。要保证操作数的数据类型一致,编译器对所选择的操作数进行提升(promotion)操作(也称为隐式类型转换,implicitconversion)。例如,在包含数据类型float和int的表达式中,int操作数提升为float。本例中,gradeCounter提升为float之后进行计算,将浮点数除法得到的结果赋给average。本章稍后将介绍所有标准数据类型及其提升顺序。任何数据类型都可用强制类型转换运算符,static_cast运算符由关键字statlccast加尖括号(<>)中的数据类型名组成。强制类型转换运算符是个一元运算符(unaryperator),即只有一个操作数的运算符。第1章曾介绍过二元算术运算符。C++也支持一元正(+)、负(-)运算符,程序员可以编写-7、+5之类的表达式。强制类型转换运算符从右向左结合,其优先级高于正(+)、负(-)运算符等其他一元运算符,该优先级高于运算符*、/和%,但低于括号的优先级。优先级表中用static_cast<type>()表示强制类型转换运算符。
图2.9中格式化功能将在第11章详细介绍,这里先做一简要介绍。下列输出语句中调用
setpreclslon(2):
cout<<"Class average is" <<setprecision(2)
<<Setiosflaqs(iOS::fixed|iOS::showpoint)
<<averaqe<<endl;
表示float变量average打印小数点右边的位数为两位精度(precision),例如92.37,这称为参数化流操纵算子(parameterizedstream manipulator)。使用这些调用的程序要包含下列预处理指令:
#include<iomanip.h>
注意endl是非参数化流操纵算子(nonparameterized streammanipulator),不需要iomanip.h头文件。如果不指定精度,则浮点数值通常输出六位精度(即默认精度,defaultprecision),但稍后也会介绍一个例外。
上述语句中的流操纵算子setiosflags(ios::fixed |ios::showpoInt)设置两个输出格式选项ios::fixed和ios::showpoint。垂直条(1)分隔setiosflags调用中的多个选项(垂直条将在第16章详细介绍)。选项ios::fixed使浮点数值以浮点格式(而不是科学计数法,见第ll章)输出。即使数值为整数,ios::showpoInt选项也会强制打印小数点和尾部O,如88.OO。如果不用ios::showpoint选项,则C++将该整数显示为88,不打印小数点和尾部o。程序中使用上述格式时,将打印的值取整,表示小数点位数,但内存中的值保持不变。例如,数值87.945和67.543分别输出为87.95和67.54。

常见编程错误2.10
如莱在使用浮点敷时认为其精确地表示了敷值.则全得到不正确的蛄柬。浮点敷雇大多数计算机上都采用近似表示。
编程技巧2.12
不要比较浮点数值的相等和不等性,而要测试差值绝对值是否小于指定的值。

尽管浮点数算不总是100%精确,但其用途很广。例如,我们说正常体温98.6(华氏温度)时,并不需要精确地表示,如果温度计上显示98.6度.实际上可能是98.5999473210643度。这里显示98.6对大多数应用已经足够了。
另一种得到浮点数的方法是通过除法。10除以3得到3.333333……,是无限循环小敷。计算机只分配固定空间保存这种值,因此只能保存浮点值的近似值。

2.10构造算法与自上而下逐步完善:实例研究3(嵌套控制结构)
下面介绍另一个问题。这里还是用伪代码和自上而下逐步完善的方法构造算法,然后编写相应的C++程序。我们介绍过按顺序堆叠的控制结构,就像小孩堆积木一样。这里显示C++中控制结构的另一种方法,称为嵌套控制结构。
考虑下列问题:
学校开了一门课,让学生参加房地产经纪人证书考试。去年,几个学生读完这门课并参加了证书考试。学校想知道学生考试情况,请编写一个程序来总结这个结果。已经得到了10个学生的名单,每个姓名后面写1时表示考试通过,写2时表示没有通过。
程序应分析考试结果,如下所示:
1.输入每个考试成绩(即l或2),每次程序请求另一个考试成绩时,在屏幕上显示消息“Enterresult"。
2.计算每种类型的考试成绩数。
3.显示总成绩,表示及格人数和不及格人数。
4.如果超过8个学生及格,则打印消息“Raisetuition”。
认真分析上述问题后,我们做出下列结论:
1.程序要处理10个考试成绩,用计数器控制循环。
2.每个考试成绩为数字l或2,每次程序读取考试成绩时,程序要确定成绩是否为数字1或2。
我们的算法中测试1,如果不是l,则我们假设其为2(本章末尾的练习会考虑这个假设的结果)。
3,使用两个计数器,分别计算及格人数和不及格人数。
4.程序处理所有结果之后,要确定是否有超过8个学生及格。

下面进行自上而下逐步完善的过程。首先是上层的伪代码表示:
Analyze exam resultsand decide if tuition should beraised
我们再次强调,顶层是程序的完整表达,但通常要先进行几次完善之后才能将伪代码自然演变成C++程序。我们的第一步完善为:
Initialize variables
lnput the ten quiz qrades andCOU~t passes and failures
Print a sugary Of the cxamresults and decide if tuition should beraised
这里虽然有整个程序的完整表达式,但还需要进一步完善。我们要提供特定变量。要用两个计数器分别计算,用一个计数器控制循环过程,用一个变量保存用户输入。伪代码语句:
Initialize variables
可以细分如下:
Initialize passes tozero
lnitialize failules to zero
Inltiallzestudent counter toOne
注意.这里只初始化计数器和总和。伪代码语句:
Input the ten quizgrades and count Passes andfaiLures
要求循环输入每个考试成绩。我们事先知道共有10个成绩,因此可以用计数器控制循环。在循环中(即嵌套在循环中),用一个双项选择结构确定考试成绩为数字1或2,并递增相应的计数器。上述伪代码语句细化如下:
while student counter is less than or equal to ten
Input the next exam result
if the studentpassed
Add one to Passes
else
Add One to failures
Addone to studentcounter
注意这里用空行分开if/else控制结构,以提高程序可读性。伪代码语句:
Print a sugary Of the exam results and declde if tuition should beraised
可以细化如下:
Print the number ofpasses
Print the number offiluies
if more than eight studentsPassed
Priht "Raise tuition"
图2.10显示了完整的第2步完善结果。注意这里用空行分开while结构,以提高程序可读性。

Initlalize passes to zero
Init±a1ize failuresto zero
lnitlallze student counter toone
while student counter is less than or equal toten
Input the next exam result
if the student Passed
Add one topasses
else
Add one tofailures
Add one to studentcounter
Priht the number ofpasses
Prirt the number offilures
if more than eight studentspassed
Print”Raisetuition'’

图2.10检查考试成绩的伪代码

这个伪代码语句已经足以转换为C++程序。图2.11显示了C++程序及示例的执行结果。注意,
我们利用C++的一个特性,可以在声明中进行变量初始化。循环程序可能在每次循环开头要求初始
化,这种初始化通常在赋值语句中进行。
1// Fig. 2.11: fig02_ll.cpp
3 #include <iostream.h>
4
5 intmain()
6 {
// initializevariables in declarations
int passes = 0, // number ofpasses
Passes = v; // number orpasses
failures = 0, // number offailures
studentCounter = 1, // studentcounter
result;// oue exam result
//process 10 students; counter-controlledloop
while ( studentCounter<= 10 ){
cout << "Enter result (1=pass,2=fail):";
cin >>result;
if { result == 1 } // if/else nested inwhile
passes = passes +1;
else
failures = failures +1;
studentcounter = studentCounter +1;
)
// terminationphase
cout << "Failed"<< failures <<endl;
if ( passes > 8)
cout << "Raise tuition "<<endl;
return 0;// successful termination

输出结果:

Enterresult (l=pass,2=fail}: 1
Enter result(l=pass,2=fail): 2
Enter result (l=pass,2=fail):1
Enter result (l=pass,2=fail):1
Enter result (l=pass,2=fail):1
Enter result (l=pass,2=fail)1
Enter result (l=pass,2=fail):2
Enter result {l=pass,2=fail):1
Enter result (l=pass,2=fail):1
Enter result )1=pass,2=fail):2
Passed 6
Failed4

Enter result (a=pass,2=Fail):1
Enter result (l=pass,2=fail):1
Enter result (l=pass,2=fail)1
Enter result (1-pass,2=fail):2
Enter result {l=pass,2=fail):1
Enter result (l=pass,2=fail):1
Enter result{1=Pass,2=fail):1
Enterresult(1=pass,2=fail):1
Enterresult(1=pass,2=fail):1
Enterresult(1=pass,2=fail):1
Passed9
Failed 1
Raisetuition

图2.11 检查考试成绩的C++程序及示例执行结果

编程技巧2.13
在声明中进行变量初初化可以帮助程序员避免数据表初始化问题。
软件工程视点2.7
经验表明,计算机问题最难解决的部分是开发解决方案的算法。一旦确定正确算法后,从算法生成C++程序的过程通常是相当简单的。
软件工程视点2.8
许多熟练的程序员不必用伪代码之类的程序开发工具即可编写程序。这些程序员认为其最终目标是解决计算机上的问题,编写伪代码只会延迟最终产品的推出。尽管这种方法在简单和热悉的问题中能行得通,但在大型复杂项目中则可能导致严重的错误和延迟。

2.11赋值运算符
C++提供了几个赋值运算符可以缩写赋值表达式。例如下列语句:
c = c +3;
可以用加法赋值运算符(addition assignmentoperator)“+=”缩写如下:
c +=3;
+=运算符将运算符右边表达式的值与运算符左边表达式的值相加,并将结果存放在运算符左边表达式的值中。下列形式的语句:
variable = variable operatorexpression;
其中operator为二元运算符+、-、/或%之一(或今后要介绍的其他二元运算符),均可写成如下形式:
variable operator =exprission;
这样,赋值语句c+=3将3与c相加。图2.12显示了算术赋值运算符、使用这些算术赋值运算符的示例表达式和说明。
性能提示2.3
使用缩写赋值运算符可以使程序更员快地编写程序,也可以使编译器更快地编译程序。有些编译器在用缩写赋值远算符时能产生运行速度更快的代码。

性能提示2.4
本书介绍的许多性能提示只产生少量改进,读者可能不会大注意。但在多次重复的循环中,少量的改进可能积累成巨大的性能改进。
赋值运算符示例表达式说明赋值 假设intc=3,d=5,e=4,f=6,g=12;

+=e+=7c=c+710赋值给e
-=d-=4d=d-41赋值d
*=e*=5e=e*520赋值给e
/=f/=3f=f/32赋值给f
%=g%=9g=g%93赋值给g
图 2.12 算术赋值运算符

2.12 自增和自减运算符
C++还提供一元自增运算符(increment operator,++)和一元自减运算符(dcrementoperator),见图2.13。如果变量c递增1,则可以用自增运算符++,而不用表达式c=c+1或c+=1。如果将自增和自减运算符放在变量前面,则称为前置自增或前置递减运算符(preincrement或predecrementope~torL如果将自增和自减运算符放在变量后面,则称为后置自增或后置自减运算撤postmcrement或postdcrementoperator)。前置自增(前置自减)运算符使变量加1(减1),然后在表达式中用变量的新值。后置自增(后置自减)运算符在表达式中用变量的当前值,然后再将变量加1(减1)。

运算符名称示例表达式说明 ++前置自增++a将a加1,然后在a出现的表达式中使用新值
++后置自增a++在a出现的表达式中使用当前值,然后将a加1
--前置自减--b将b减1,然后在b出现的表达式中使用新值
--后置自减b--在b出现的表达式中使用当前值,然后将b减1
图 2.13自增和自减运算符

图2.14的程序演示了++运算符的前置自增与后置自增计算之间的差别,后置自增变量c使其在输出语句中使用之后再递增,而前置自增变量c使其在输出语句中使用之前递增。

1//Fig, 2.14:fig02 14.cpp
2 // Preincrementing and postincrementing
3#include <iostream.h>
4
5 int main()
6{
7 int c;
8
9 C =5;
10 cout << C <<endl; // print 5
11cout << C++ << endl; // print 5then postincrement
12 cout << c << endl<< endl; // print
13
14 c = 5;
15cou << c << endl; // print5
16 cout << ++c <<endl; // preincrement then print6
17 cout << c << endl;
18
19return O; // successfulterminatiOn
20}

输出结果:
5
5
6

5

6
6

图2.14 前置自增与后置自增计算之间的差别

程序显示使用++运算符前后的c值,自减运算符的用法类似。

编程技巧2.14
一元运算符及其操作数之间不能插入空格。

图2.11的三个赋值语句:
passes=passes+1;
failures=failures+1
student=student十1;
可以改写成更简练的赋值运算符形式:
passes+=1;
failures+=1;
student+=1;
使用前置自增运算符,如下所示:
++passes;
++failures;
++student;
或使用后置自增运算符,如下所示:
passes++
failures++
student++
注意,单独一条语句中自增或自减变量时,前置自增与后置自增计算之间的的结果一样,前置自减与后置自减计算之间的结果也相同。只有变量出现在大表达式中时,才能体现前置自增与后置自增计算之间的差别(和前置自减与后置自减计算之间的差别)。
目前只用简单变量名作为自增和自减的操作数(稍后会介绍,这些运算符也可以用于左值)。

觉编程错误2.11
要用非简单变量名表达式(如++(x+1))作为自增和自减运算符的操作数是个语法错误。

图2.15显示了前面所介绍的运算符优先级和结合律,从上到下,优先级依次递减。第二栏介绍每一级运算符的结合律,注意条件运算符(?:)、一元运算符自增(++)、自减(--)、正(+)、负(-)、强制类型转换以及赋值运算符(=、+=、-;、*=、/=和%=)的结合律为从右向左。图1.15中所有其他运算符的结合律为从左向右。第三栏是运算符的组名。

运算符结合律类型 ()括号
++ -- + -static_cast<type>()从左向右一元
* /%从右向左乘
+-从左向右加
<<>>从左向右插入/读取
< <=>>=从左向右关系
==!=从左向右相等
?:从右向左条件
= += -=*= /=%=从右向左赋值
,从左向右逗号


图2.15 前面所介绍的运算符优先级和结合律

2.13 计数器控制循环的要点
计数器控制循环要求:
l.控制变量(或循环计数器)的名称(name)。
2.控制变量的初始值(initialvalue)。
3.测试控制变量终值(finalvalue)的条件(即是否继续循环)。
4.每次循环时控制变量修改的增量或减量(incrementdecrement)。

考虑图2.16所示的简单程序,打印1到10的数字。声明:
int counter =1;
指定控制变量(counter)并声明为整数,在内存中为其保留空间并将初始值设置为1。需要初始化的声明实际上是可执行语句。在C++中,将需要分配内存的声明称为定义(definition)更准确。

1// Fig. 2.16: fig02_16.cpp
2 // Counter-controlled repetition
3#include <iostream.h>
4
5 int main()
6{
7 int counter =1; //initialization
6
9 while ( counter <= 10 ){ // repetition condition
10 cout<< counter << endl;
11++counter;// increment
12 }
13
15 }

输出结果:
2
4
5
0
8
9
10

图2.16计数器控制循环


counter的声明和初始化也可以用下列语句完成:
intcounter;
counter=1;
声明不是可执行语句,但赋值是可执行语句。我们用两种方法将变量初始化。
下列语句:
++counter;
在每次循环时将循环计数器的值加1。while结构中的循环条件测试控制变量的值是否小于或等于10
(条件为true的终值)。注意,即使控制变量是10时,这个while结构体仍然执行。控制变量超过10
时(即counter变成11时),循环终止。
图2.16的程序也可以更加简化,将counter初始化为。并将while结构换成:
while(++counter <= lO)
cout << counter <<endl;
这段代码减少了语句,直接在while条件中先增加计数器的值再测试条件。这段代码还消除了while
结构体的花括号,因为这时while只包含一条语句。

常见编程错误2.12
由于浮点值可能是近似值,用浮点变量控制计数循环可能导致不精确的计数器值,使测试的结果不准确。
编程技巧2.15
用整数值杜制计数循环。
编程技巧2.16
缩排每个控制结构体中的语句。
编程技巧2.17
在每个控制结构前后加上空行,使其在程序中一目了然。
编程技巧2.18
嵌套太多会使程序难以理解。一般来说,缩排不宜超过三层。
编程技巧2.19
在每个控制结构前后加上空行,并缩排每个控制结构体中的语句使程序产生二维效果,大大增加可读性。

2.14for重复结构
for重复结构处理计数器控制循环的所有细节。要演示for的功能,可以改写图2.16的程序,结果如图2.17。
执行for重复结构时,声明控制变量counter并将其初始化为1。然后检查循环条件counter<=10。由于counter的初始值为1,因此条件满足,打印Counter的值(1)。然后在表达式Counter++中递增控制变量counter,再次进行循环和测试循环条件。由于这时控制变量等于2,没有超过最后值,因此程序再次执行语句体。这个过程一直继续,直到控制变量counter递增到11,使循环条件的测试失败,重复终止。程序继续执行for结构后面的第一条语句(这里是程序末尾的return浯句)。

1// Fig. 2.17:fig02 17.cpp
2 // Counter-controlled repetition with thefor structure
3 #include <iostream.h>
4
5 int main()
6{
7 // Initialization, repetition condition, andincrementing
8 // are all included in the forstructure header.
9
10 for ( iht counter = 1; counter<= 10; counter++ )
11 cout << counter<< endl;
12
13 return O;
14}

图 2.17用for结构的计数器控制重复

图2.18更进一步研究了图2.17中的for结构。注意for结构指定计数器控制重复所需的每个项目。如果for结构体中有多条语句,则应把语句体放在花括号中。
注意图2.17用循环条件counter<=10。如果循环条件变为counter<lO,则循环只执行9次,这种常见的逻辑错误称为差1错误。

for
关键字控制变量名控制变量终值
↓↓↓
for( int counter = 1; counter <= 10;counter++)
-------------
↑↑↑
控制变量初始化循环条件控制变量递增

图2.18典型for首部的组件

常见编程错误2.13
while或for条件中使用不正确的关系运算符和不正确的循环计数器终值会导致差1的错误。

编程技巧2.20
在while或for条件中使用终值和用关系运算符<=可以避免差1的错误。例如,对打印1到10的循环,循
环条件应为counter<=10而不是counter<10(导致差1错误)或counter(虽然正确)。许多程序员喜欢基
于O的计数,在循环中重复10次.courter初姑化为O,循环条件测试为counter<10。
for结构的一般格式如下:
for(expression1;expression2;expression3)
statement
其中expression1初始化循环控制变量的值,expression2是循环条件,expression3递增控制变量。大
多数情况下,for结构可以表示为等价的while结构:
expression1
while(expression2){
statement
expression3;
}
惟一的例外将在2.18节介绍。
如果for结构首部中的expression1(初始化部分)定义控制变量(即控制变量类型在变量名前
面指定).则该控制变量只能在for结构体中使用,即控制变量值是for结构之外所未知的。这种限
制控制变量名的用法称为变量的作用域(scope)。变量的作用域定义其在程序中的使用范围。作用
域将在第3章“函数”中介绍。

常见编程错误2.14
如果for结构首部中的初始化部分定义控制变量,则在该结构体之后使用这个控制变量是个语法错误。
可移植性提示2.1
在新的C++草案标准中,for结构初始化部分声明的控制变量范围与旧式的C++编译器中不同。旧式的c++编译器产生的C++代码在支持新的C++草案标准妁编译器中编译时可能遭到破坏。可以用两个编工程策略防止这个问题:在每个for结构中定义不同名称的控制变量或者在多个for结构中定义相同名称的控制变量,并在第一个for循环之外和之前定义控制变量。
有时,exprossion1和expression3是由逗号分开的表达式列表。这里用逗号作为逗号运算符(commaoperator),保证从左向右求值表达式列表。逗号运算符在所有C++运算符中的优先级量低。逗号分隔表达式列表的值和类型是列表中最右边表达式的值和类型。逗号运算符最常用于for结构,其主要用途是让程序员使用多个初始化表达式或多个递增表达式。例如,一个for结构中可能有多个控制变量需要初始化和递增。

编程技巧2.21
只把涉及控制变量的表达式放在for结构的初始化和递增部分。其他变量的操作应放在循环之前(如果像初初化语句一样只执行一次)或循环体中(如果对每个循环环执行一次,如递增和递减语句)。
for结构中的三个表达式是可选的。如果省略expression2,则C++假设循环条件为真,从而生成无限循环。如果程序其他地方初始化控制变量,则可以省略expression1。如果for语句体中的语句计算增量或不需要增量,则可以省略expression3。for结构中的增量表达式就像是for语句体末尾的独立语句。因此,下列表达式:
counter = counter + 1;
counter +=1;
++counter;
counter++;
在for结构的递增部分都是等价的。许多程序员喜欢counter++,因为递增在执行循环体之后发生,因此,后置自增形式似乎更自然。由于这里递增的变量没有出现在表达式中,因此前置自增与后置自增的效果相同。for结构首部中的两个分号是必需的。

常见编程错误2.15
for结构首部中的两个分号改成逗号会造成语法错误。
常见编程错误2.16
将分号放在for结构首部的右括号后面令使该for结构体变为空语句.通常是个逻辑错误。
软件工程视点2.9
将分号放在紧接着for结构首部的后面有时可以生成所谓的延迟循环。这种for的循环体是空语句,表示计算空循环的次数。例如,可以用空循环减慢程序速度,以避免其在屏幕上输出太快,无法阅读。

for结构的初始化、循环条件和递增部分可以用算术表达式。例如,假设x=2和y=10,如果x和y的值在循环体中不被修改,则下列语句:
for (int j= x; j <= 4* x* y; j += y/ x)
等于下列语句:
for(intj=2;j<=80;j+=5)
for结构的增量也可能是负数(实际上是递减,循环向下计数)。
如果循环条件最初为false,则for结构体不执行,执行for后面的语句。
for结构中经常打印控制变量或用控制变量进行计算,控制变量常用于控制重复而不在for结构
体中提及这些控制变量。

编程技巧2.22
尽管控制变量值可以在for循环体中改变,但最好不要这样做,固为这样可能造成一定的逻辑错误。
for结构的流程图与while结构相似。例如,图2.19显示了下列for语句的流程图:
for(intcounter=l;counter>=10;counter++)
cout<<counter<<endl;
从这个流程图可以看出初始化发生一次,井在每次执行结构体语句之后递增。注意,流程图(除了小圆框和流程之外)也只能包含表示操作的矩形框和表示判断的菱形框。这是我们强调的操作/判断编程模型。程序员的任务就是根据算法使用堆栈和嵌套两种方法组合其他几种控制结构,然后在这些框中填入算法所要的操作和判断,从而生成程序。


2.15for结构使用举例
下面的例子显示for结构中改变控制变量的方法。在每个例子中.我们都编写相应的for结构首部。注意循环中递减控制变量的关系运算符的改变。
a)将控制变量从1变到100,增量为1。
for(inti=l;i<=100;i++)
b)将控制变量从100变到1,增量为-1。
for(inti=100;i>=1; i--)

常见编程错误2.17
循环向下计数时如果循环条件中不使用正确的关系运算符(如在向下计算到1的循环中使用i<=1)通常是个逻辑错误,会在程序运行时产生错误结果。

c)控制变量的变化范围为7到77。
for(int i= 7;i <= 77; i+= 7)
d)控制变量的变化范围为20到2。
for(inti=20;i>=2;i-=2)
c)按所示数列改变控制变量值:2、‘、8、11、14、17、20。
for(int j=2;j<=20;j+=3)
f)按所示数列改变控制变量值:99、88、77、66、55、44、33、22、ll、O。
for(int j=99; j>=O; j-=11)
下面两个例子提供for结构的简单应用。图2.20所示的程序用for结构求2到100的所有整数的总和。
注意图2.20中for结构体可以用下列逗号运算符合并成for首部的右边部分:
for(intnumber =2;// initialization
number<=100;// continuation condition
sum += number, number +=2) // total andincrement
初始化sum=0也可以合并到for的初始化部分。

1// Fig. 2.20:figO2_20.cpp
2 // Summation with for
3 #include<iostream.h>
4
5 int main()
6 {
7 intsum = O;
8
9 for (int number = 2; number <=100; number += 2 )
10 sum +=number;
11
12 cout << "Sum is "<< sum<< endl;
13
14 return O;
15 }

输出结果:
sum is2550
图2.20 用for语句求和

编程技巧2.23
尽管for前面的语句和for结构体的语句通常可以合并到for的首部中,但最好不要这么做,因为这样会使程序更难阅读。
编程技巧1.24
尽可能将控制结构首部的长度限制为一行。
下列用for结构计算复利。考虑下列问题:
一个人在银行存款1000.00美元,每利率为5%。假设所有利息留在账号中,则计算10年间每年年末的金额并打印出来。用下列公式求出金额:
a=P(1+r)n
其中:
P是原存款(本金)
r是年利率
n是年数
a是年未本息
这个问题要用一个循环对10年的存款进行计算。解答如图2.21
for结构执行循环体10次,将控制变量从1变到10,增量为1。C++中没有指数运算符,因此要用标准库函数pow。函数pow(x,y)计算x的y次方值。函数pow取两个类型为double的参数并返回double值。类型double与float相似,但double类型的变量能存放比float精度更大的数值。C++把常量(如图2.21中的1000.0和.05)当作double类型处理。
1// Fig. 2.21: fig02_21.cpp
2 // Calculating compound interest
3#include <iostream.h>
4 #include <iomanip.h>
5 #include<math.h>
6
7 int main()
8 {
9 doubleamount, //amount on deposit
10principal = 1000.0, // startingprincipal
11 rate =.05; // interestrate
12
13 cout << "Year" << setw( 21)
14 << "Amount on deposit"<< endl;
15
16 for ( int year = 1; year <= 10;year++ ) {
17 amount = principal * pow{ 1.0 +rate, year );
10 cout << setw( 4 )<< year
19<< setiosflags( ios::fixed I ios::showpoint)
20 << setw( 21) << setprecision( 2)
21 << amount<< endl;
22 }
23
24
25}

输出结果:
Year Amount ondeposic
11050.00
21102.50
31157.62
41215.51
51276.28
61340.10
71407.10
81477.46
91551.33
101628.89

图 2.21用for结构计算复利

这个程序必须包括math.h才能编译。函数pow要求两个double参数,注意year是个整数。math.h文件中的信息告诉编译器将year值转换为临时double类型之后再调用函数。这些信息放在pow的函数原型(functionprototype)中。第3章将介绍函数原型并总结pow函数和其他数学库函数。

常见编程错误1.18
程序中使用数学库函数而不包括math.h头文件是个语法错误。

程序中将变量amount、principal和rate声明为double类型,这是为了简单起见,因为我们要涉及存款数额的小数部分,要采用数值中允许小数的类型。但是,这可能造成麻烦,下面简单介绍用float和double表示数值时可能出现的问题(假设打印时用setprecision(2)):机器中存放的两个float类型的值可能是14.234(打印14.23)和18.673(打印18.67)。这两个值相加时,内部和为32.907,打印32.91。结果输出如下:
14.23
+ 18.67
-----------
32.91

但这个加式的和应为32.90。

编程技巧2.25
不要用float和double表示一些货币值。浮点数是不精确的,可能导致错误,产生不准确的货币值。练习中将介绍用整数值进行货币计算。注意:c++类库可以用于正确地进行货币计算。
输出语句:
cout<<setw(4)<<year
<<setiosflags(10s::fixed los::showpoint)
<< setw(21) << setpreclsion(2)
<<amount<<endl;
用参数化流操纵算于setw、setiosflags和setprecision指定的格式打印变量year和amount的值。调用selw(4)指定下一个值的输出域宽(fieldwidth)为4,即至少用4个字符位置打印这个值。如果输出的值宽度少于4个字符位,则该值默认在输出域中右对齐(ishljustl^ed),如果输出的值宽度大于4

第1章_编程简介



出处:C++大学教程

第1章计算机与C++编程简介

教学目标

●了解计算机科学的基本概念
●熟悉不同类型的编程语言
●了解典型C++程序的开发环境
●用C++编写简单的计算机程序
●使用简单输入与输出语句
●熟悉基本数据类型
●使用算术运算符
●了解算术运算符的优先级
●编写简单的判断语句

1.1 简介

欢迎来到C++的世界!我们将努力带给你一个信息丰富、充满趣味和富于挑战的学习经历。C++是一种较难的语言,通常只传授给有经验的程序员,因此本书在C++教材中独具一格:

●适合很少或没有编程经验的技术方面的人员
●适合需要深入钻研C++的熟练的程序员

一本书怎样适应两类读者呢?本书始终强调通过实践证明的结构化编程〔structuredprogramming)和面向对象编程(object-orientedprogramming)技术编写清晰的程序。非程序员从一开始就要养成良好的编程习惯。我们尽量以清晰而直接的方式编写程序。本书附有大量插图,更重要的是.本书提供大量实用的C++程序,并显示这些程序在计算机上运行时产生的输出结果。所有C++特性都是在完整、可工作的C++程序环境中介绍的,我们称其为“有生命力的代码”。所有这些例子都可以从我们的web站点www.deitel.com中下载.也可以通过本书所配的交互式光盘《C与C++多媒体教室(第二版)》中取得。多媒体教室的特性见本书最后的说明。多媒体教室中还包含本书一半练习的解答,包括简单解答、小型程序和许多完整项目。
本书的前五章介绍计算机基础、计算机编程和C++计算机编程语言。参加我们课程的新学员告诉我,第1章到第5章的材料为今后学习C++的高级知识打下了坚实基础。熟练的程序员可以快速浏览前五章,然后阅读本书其余部分对C++的有深度、有挑战性的介绍。
许多熟练的程序员告诉我们,他们很欣赏我们对结构化编程的处理、他们通常用C或Pascal结构化编程语言进行编程,但由于没有系统地学习结构化编程,因此常常无法用这些语言编写出最佳代码。学习本书前几章介绍的结构化编程知识后,有助于改进使用C或Pascal语言编程的风格。因此,无论你是新手还是熟练的程序员,这里的信息都是充实、有趣并且具有一定深度的。
大多数人对计算机能做的工作略有所知,利用本书可以学会如何指示计算机做这些工作。软件(sofware,即编写的指令,命令计算机完成操作并做出判断)可以控制计算机(通常称为硬件,Hardware)。C++是当今最常用的软件开发语言之一。本书介绍的C++版本已经过美国国家标准协会(ANSI,Americanational Standards Institute)和国际标准化组织(IS0,InternationalStanards0rganization)的标准化,这个ANSI/ISOC++草案标准已经批准为全球标推。
计算机的应用领域在不断增加。在这个成本稳步攀升的时代,计算成本却在急速下降,因为硬件和软件技术都在飞速发展。25年前装满整个房间、价值上百万美元的计算机如今已经缩小到比手指甲还小的芯片,而且只要几美元。具有讽刺意味的是,硅是地球上最丰富的资源之一,是普通砂子的主要组成部分。硅芯片技术使计算技术如此经济,如今全世界已经有大约2亿台通用计算机在使用中,其应用涉及商业、工业、政府和个人生活。这个数字在近年内必将翻番。
本书将使读者面临几个挑战。几年前,人们学习第一个编程语言时可能只要学习C或Pascal,但实际上还需要学习C和C++,为什么呢?因为C++包括c语言和其他更多的内容。
几年前人们只需学习结构化编程(sstructuredprogramming),而今则既要学习结构化编程,又要学习面向对象编程(object-orientedprogramming),因为面向对象是今后10年最关键的编程方法。本课程要建立和使用许多对象(Object),但是这些对象的内部结构最好用结构化编程方法建立。另外,操作对象的方法最好也用结构化编程方法来表达。
另一个介绍两种方法的原因是,目前有大量C++系统是从c语言系统移植过来的,还有大量的所谓“C语言遗留代码”。C语言已经使用20余年,近年来用得越来越多。人们学习C++之后就会发现,C++比C语言强得多,因此通常会转到使用C++.他们会将遗留的系统移植到C++,这是个相对简单的过程。然后,他们开始用各种C++对C++语言的增强特性改进其编写的类C语言程序的风格。最后,他们开始利用C++面向对象编程的功能,真正了解这种语言的全部好处。
编程语言中的一个有趣现象是,大多数厂家都推出C/C++产品组合而不是提供分开的产品。这样,用户可以继续使用C语言编程,适当时候再逐渐过渡到C++。
C++已经成为实现语言的首选.但它能在第一门编程课程中介绍吗?我们认为可以。五年前,当人们用Pascal作为第一门编程课程时,我们遇到过类似挑战。我们编写了《CHow To Program》。
如今全世界几百所大学都在使用《C How ToPrrogram》第二版,使用这本教材的课程和使用Pascal作为第一门编程课程一样有效。其间没有明显的差别,只是学生学习的兴趣更高,因为他们知道工作中要使用的是c语言而不是Pascal语言。学习C语言的学生还能更快地学习C++和新的Internet语言——Java。
本书前五章介绍C++中的结构化编程方法、C++的“C语言部分”和“C++对C语言的改进”。然后要介绍C++面向对象编程,但我们不想等到第6章再介绍面向对象编程,因此前五章每一章都有一节“有关对象的思考”,介绍面向对象编程的基本概念和术语。第6章“类与数据抽象“将开始用C++生成对象并编写面向对象的程序。
第1章分为三部分,第一部分介绍计算机基础和计算机编程。第二部分立即开始编写一些简单C++程序,第三部分介绍有关对象的思考。
下面要开始富有挑战和回报的旅程了。学习过程中,如果想与我们联系,可以给我们发电子邮件

deltel@deitel.com

或浏览我们的web站点:

http://www.deitel.com

我们将立即答复。希望大家喜欢学习《C++大学教程》,还可以使用本书的交互式光盘版本《C与C++多媒体教室(第二版)》,详见本书最后的说明。

1.2 什么是计算机

计算机(computer)是能以人的几百万甚至几十亿倍速度进行计算井作出逻辑判断的设备。例如.今天的许多个人计算机每秒钟可以进行几亿次加法运算。操作台式计算器的人要几十年才能算出的数值,强大的个人计算机只要一秒钟即可计算完毕(注意:你怎么知道这个人加对了没有?你怎么知道计算机做得是否正确?)。如今.最快的超级计算机(supercomputer)每秒钟可以进行几干亿次加法运算,是成百上千的人花一整年时间才能完成的计算工作。每秒钟万亿条指令的计算机已经能在研究实验室中工作。
计算机在一组指令控制下处理数据(data),这组指令称为计算机程序(computerprogrammer)。这些计算机程序指导计算机按顺序进行计算机程序(computerprogrammer)指定的一组操作。
构成计算机系统的各种设备(如键盘、屏幕、鼠标、磁盘、内存、光盘和处理器)称为硬件。计算机上运行的计算机程序称为软件。几年来,硬件成本已经大幅下降,使个人计算机更加平民化。但是,随着程序员开发了许多越来越强大、越来越复杂的应用程序,而软件开发技术却进步不大,因而使软件开发成本不断上升。本书介绍通过成熟的软件开发方法减少软件开发成本,即结构化编程、自上而下逐步完善、功能化以及面向对象编程。

1.3计算机组成
不管外观如何不同,每个计算机都可以看成由六个单元(logical unit)或部分组成,即:

1.输入单元(iput unit),这是计算机的“接收”部分,从各种输入设备接收信息(数据和计算机程序),并将这些信息放到其他单元中,使信息得以处理。如今大多数信息都是通过键盘和鼠标设备输入计算机。将来大多数信息也许可以通过语音输入或扫描图形而获得。
2.输出单元(outputunit),这是计算机的“发送”部分。将计算机处理过的信息送到不同输出设备中,向计算机外部提供所需的信息。如今计算机输出的大多数信息是通过屏幕显示、书面打印或用于控制其他设备。
3.内存单元(memoryunit),这是计算机中快速访问、低容量的“库存”部分。它保存通过输入单元输入的信息,以便在需要时立即提供这些信息进行处理。内存单元保存处理的信息,直到输出单元将信息故到输出设备中。内存单元也称为内存或主内存(memory或primarymemory)。
4.算术/逻辑单元(arithmetic and logic unitALU)这是计算机的“生产”部分,负责进行加、减、乘、除等运算,包含判断机制例如可以让计算机比较内存单元中的两个项目,确定其是否相等。
5.中央处理单元(centralprocessing unitCPU),这是计算机中的“管理”部分,是计算机的协调员,负责管理其他部分的操作。CPU告诉输入单元何时将信息读取到内存单元中,告诉ALU何时利用内存单元中的信息进行计算,告诉输出单元何时将内存单元中的信息发送到指定的输出设备中。
6.辅助存储单元(secondarystorageunit),这是计算机长期的高容量“库存”部分,其他单元不是经常使用的程序或数据通常放在辅助存储单元(如磁盘)中,直到几小时、几天、几月甚至几年后才需要。访问辅助存储单元中的信息要比访问主内存中的信息慢得多。辅助存储单元的单位成本比主内存的单位成本低得多。

1.4 操作系统的变革
早期计算机一次只能完成一个任务或作业(task或job),这种计算机操作通常称为单用户批处理(batchprocessing)。计算机一次运行一个程序,成组或成批地处理数据。在这些早期系统中,用户利用穿孔卡片将作业提交到计算机中心,通常要等待几小时或几天之后才能得到打印输出。
称为操作系统(operatingsystem)的软件系统可以帮助用户更方便地使用计算机。早期操作系统能管理作业之间的顺利过渡,使得计算机操作员在作业之间切换的时间减到最少,从而增加计算机处理的工作量或吞吐量(throughput)。
随着计算机的功能越来越强大,单用户批处理机制显然不能有效地利用计算机资源,因此应该让许多任务或作业共享计算机资源,以达到更好地利用资源,这种方法称为多道程序设计(multiprogramming)。多道程序系统涉及多个作业在计算机上“同时”操作,计算机在竞争资源的作业之间共享资源。在早期多道程序操作系统中,用户还是要通过穿孔卡片将作业提交到计算机中心,几小时或几天之后才能得到打印输出。
20世纪60年代,计算机界和大学的几个研究小组提出了分时(timesharing)操作系统。分时是多道程序的特殊情况,用户通过终端(terminal)访问计算机,终端是带有键盘和屏幕的典型设备。在典型的分时操作系统中,可能有几十甚至几百个用户同时共用计算机。计算机实际上并不是同时运行所有用户,而是运行一个用户的一小段作业,然后转入运行下一个用户的一小段作业。计算机的速度非常快,每秒钟可以为每个用户服务多次,使得用户的程序看上去是在同时运行。分时的好处之一是用户能立即收到响应,而不必像原先的计算方式需要等待很长时间。

1.5个人计算、分布式计算与客户/服务器计算
1977年,Apple计算机公司使个人计算(personalcomputer)得以普及。最初,拥有一台计算机只是爱好者的梦想,随着它的价格不断降低,人们可以购买供个人或办公使用的计算机。1981年,世界上最大的计算机广家IBM公司推出了IBM个人计算机(IBMPersonal computer)。一夜之间,个人计算机遍布公司、企业和政府机关。
然而这些计算机只是“独立”的个体,各自做自己的工作,要通过磁盘复制来共享信息(通常称为暗联网)。尽管早期个人计算机不够强大,不能同时服务于多个用户,但这些机器可以链接在计算机网络中,可以接入组织内的局域网(Localarea Network,LAN),还可以通过单位内部的电话线完成链接。这样就在组成化计算中出现了分布式计算(distributedcomputing)结构,其处理不是在某个中央计算机上进行,而是由分布于网络中的机器完成。个人计算机已经足够强大,能够处理个人用户的计算要求并处理电子信息传递等基本通信任务。
如今,最强大的个人计算机已经可以和十年前几百万美元的机器相媲美。最强大的台式计算机(称为工作站,workstation)对个人用户提供了大量的功能。在网络上.有些计算机向遍布整个网络的客户提供数据存取服务,这些计算机称为文件服务器(fileserver)。通过这种方式.在网络上共享信息很容易,因而产生了客户—服务器结构oC和C++已经成为编写操作系统、计算机网络和分布式客户/服务器应用程序软件的首选编程语言。如今最常见的操作系统如UNIX、Microsoft的基于windows系统和IBM的os/2都提供了本节介绍的功能。

1.6机器语言、汇编语言和高级语言

程序员用各种编程语言编写指令,有些是计算机直接理解的,有些则需要中间翻译(tranlation)的步骤。如今使用的计算机语言有几百种,可以分为三大类:
1.机器语言
2.汇编语言
3.高级语言
任何计算机只能直接理解本身酌机器语言(machinelanguage)。机器语言是特定计算机的自然语言,由计算机的硬件设计定义。机器语言通常由一系列数字组成(最终简化0和1),让计算机一次一个地执行最基本的操作。机器语言非常繁琐,下面的机器语言程序将工龄工资和基础工资相加,并把结果保存在工资总额中:
+1300042774
+1400593419
+1200274027
随着计算机越来越普及,机器语言编程对大多数程序员显然太慢、太繁琐。程序员不用计算机直接理解的一系列数字,而是用类似英文缩写的助记将来表示计算机的基本操作,这些助记符构成了汇编语言(assemblylanguage)。称为汇编器(assembler)的翻译程序以计算机的速度将汇编语言程序转换为机器语言。下列汇编程序也是工龄工资和基础工资相加,并将结果保存在总工资中,但要比相应的机器语言清晰得多:
LOAD BASEPAY
ADD OVERPAY
STOREGROSSPAY
尽管这种代码对于人们一目了然,但计算机却无法理解,必须先翻译为相应的机器语言。
随着汇编语言的出现,计算机的使用迅速增加,然而即使是最简单的任务.也需要许多条指令才能完成。为了加速编程过程,人们开发了高级语言(high-levellanguage),用一条语句完成大量任务。称为编译器(compiler)的翻译程序将高级语言程序变为相应的机器语言。高级语言使程序员能够编写更像英语的指令,可以包含常用的数学符号。将工龄工资和基础工资相加.并把结果保存在总工资中,可以用下列高级语言程序:
grossPay = basePay + overTimePay
显然,从程序员角度看,高级语言比机器语言和汇编语言都要强得多。C和C++是最强大最广泛使用的高级语言。
将高级语言程序编译为相应的机器语言的过程可能需要大量时间。解释器(interpreter)程序可以直接执行高级语言程序,而不必先将这些程序编译成相应的机器语言。尽管编译程序的执行速度比解释程序更快,但解释器在程序开发环境中更常用,因为增加新特性和纠正错误时经常需要重
新编译程序。一旦程序开发完成,编译版本的运行最有效。

1.7C语言与C++的历史

C++是从C语言演变而来的,而C语言又是从两个编程语言BCPL和B演变而来的、BCPL是MartinRichards于1967年开发的,用于编写操作系统软件和编译器。KenThompson在他的B语言(1970年在贝尔实验室)。DCPL和B都是“无类型”语言,每个数据项在内存中占一个“字”(word)长、如果要将数据项作为整数或实数处理,编程的工作量会很大。
C语言是从D语言演变而成的,由贝尔实验室的Dennis Ritchie开发,最初于1972年在DECPDP—11计算机上实现。C语言使用了BCPL和B的许多重要概念,同时增加了数据类型和其他特性。C语言最初作为UNIX操作系统的开发语言而闻名于世。如今,大多数操作系统都是用C/C++写成的。二十多年来,C语言已经遍布在大多数计算机上。C语言是硬件无关的,只要仔细设计。就可以编写能移植列大多数计算机上的C语言程序。
到20世纪70年代未期,C语言演变成现在所谓的“传统C”、“经典CPP或“Kernighan/RitchieC"。1978年PrenticeHall公司出版了Kernighan和Ritchie合作的著作《The CProgrmmmingLanguage》,引起了人们对C语言的广泛关注(见参考文献Ke78)。
C语言在各种不同类型计算机(有时称为硬件平台)上的普及导致了许多变形。它们虽然相似,但通常互不兼容。对需要为不同平台编写可移植程序的开发人员.这是个严重问题,显然需要有个标准的C语言版本。1983年,美国国家计算机与信息处理标准委员会(x3)成立了x3JII技术分会,目的是提供无歧义性且与机器无关的语言定义。1989年推出了这种语言标准。AN5I与国际标准化组织(IS0)合作.在全球范围内将C语言标淮化,1990年推出了联合标准文档.称为ANSI/IS09899:1990。这个文档可以从ANSI获得副本。1988年推出的Kernighan和Ritchie著作的第二版体现了该版本(称为ANSI C),这也是目前全世界使用的版本(见参考文献Ke88)o

可移植性提示1.1
由于c语言是标准化、硬件无关、广为使用的语言,因此用C语言编写的应用程序通常只要稍作修改或不经修改即可在多种不同的计算机系统中运行。
C++是C语言的扩展,是20世纪80年代初由贝尔实验室的Bjarnestroustrup开发的。C++的许多特性是从c语言中派生的,但更重要的是,它提供了面向对象编程(object-orientedprogramming)的功能。
软件业正在酝酿一场革命,最终日标是更快、更正确、更经济地建立软件,新的、更强大的软件需求迫在眉睫。对象(object)实际上是模拟实际项目的可复用软件组件(component)。软件开发人员发现,利用模块化、面t向对象的设计和实现方法与过去结构化编程方法相比较,可以使软件开发小组的生产率更高。面向对象编程的优势在于更容易理解、纠正和修改。
许多面向对象的语言也纷纷涌现,包括最著名的由Xerox的Palo Alto研究中心(PARC)开发的smalltalk。Smalltalk是纯粹的面向对象的语言,其所有的编程元素都是“对象”。C++则是一种“混合型语言“,可以用C语言方式、面向对象方式或兼用两种方式进行编程。1.9节将介绍基于C/C++的新语言——Java。

1.8 C++标准库
C++程序由类(class)和函数(function)组成。可以用多个小的软件模块构成C++程序,但大多数C++程序员会利用C++标准库中已有的类和函数来编程。这样,C++“世界”中实际要学习两方面的知识,第一是学习C++语言本身,第二是学习如何利用C++标准库中现有的类和函数(本书将介绍许多类和函数)。Plauger(见参考文献P192)的著作是程序员必读的.可以帮助程序员深入了解C++中包括的ANSIC语言库函数,了解如何实现这些库函数,还可以了解如何用库函数编写可移植代码。标准库函数通常由编译器厂家提供。许多独立软件供应商(indepandentsofterware vender)也提供各种专用类库。
软件工程视点1.1
使用构件块方法(building blockapproach)生成程序,而不要事事从0开始。尽量利用现有程序块,这称为软件复用(softwarereuse),是面向对象编程的核心。
软件工程视点1.2
C++编程中通常使用下列构件块:C++标准库中的类和函数.自己生成的类和函数和各种常见非C++标准库中的类和函数。
自己生成类和函数的优点在于知道其如何工作,可以检查C++代码:缺点是要花大量时间及精力来设计、开发和维护这些类或函数,使其正确、有效地运行。
性能提示1.1
利用标准库函数和类而不用自己的对应版本可以提高软件性能,因为这些软件经过认真编写,能保证有效操作。

可移植性提示1.2
利用标准库函数和类而不用自己的对应版本可以提高软件性能,因为几乎所有C++版本都包括这些软件。

1.9Java、Internet与万维网
这是C++程序员应当高兴的时刻,整个计算机行业都为新的ANSI/ISO草案标准最终得到批准而感到兴奋。
]995年5月,Sun公司宣布推出新的java编程语言。sun公司是基于UNIX的高性能工作站的领导厂家,一直强调计算机网络的重要性。Java是基于C/C++的语言,加入了许多面向对象编程的特性。sun公司提供了基本Java软件、文档、教程和演示,可以在Web站点www.javasoft.com免费取得。Java具有大量的类库,包括支持多媒体、网络、图形、数据库访问、分布式计算等软件组件。
Java最吸引入的属性之一是它的可移植性,可以在一台计算机上编写Java程序.然后在任何支持Java的计算机上运行(目前大多数常见计算机系统都支持Java)。这对软件开发人员特别具有吸引力,他们可以不再为不同类型的计算机系统开发或维护不同的软件版本。为不同类型的计算机系统开发和维护不同的软件版本是非常费时、费力的,使得独立软件供应商只能生产常见系统平台中使用的软件,如MicrosoftWindows。如今,基于Java的软件应用程序可以在所有常用的Microsoftwidowe版本中和各种UNIX、Macintosh及OS/2等大多数流行的系统平台中运行。
我们体会到Java在学校和企业客户中的重要性,因此编写了几本Java教材和基于交互式光盘的学习软件包。这个语言变化得很快,因此我们在第一版《JavHowtoProgram》推出后仅11个月又推出了其第二版。C++要成熟很多(是1980年创建的),因此《C++大学教程》在第一版推出三年半之后才推出第二版,以配合ANSI/ISOC++草案标准的推广。

1.10其他高级语言
高级语言有数百种,但被广泛采用的只有少数几种。(COmmon Business OrientedLanguage)是1954到]957年之间由IBM公司开发的,在需要复杂数学计算的科学和工程项目中应用较多。FORTRAN仍然在工程领域广为使用。
COBOL(COmmon Business OrientedLanguage)是1959年由计算机制造商、政府和工业企业计算机用户开发的。COBOL擅长于需要精确和有效地操作大量数据的商业应用。因此,有大量的企业软件是用COBOL编写的。
Pascal(由Niklaus Wirth 教授设计)是与c语言同期出现的在涫术界应用较多,下一节将详细介绍Pascal语言。

1.11结构化编程
20世纪60年代,许多大型软件的开发遇到了严重困难。常常推迟软件计划,因而使成本大大超过预算,而且最终产品也不可靠。人们开始认识到,软件开发是项复杂的活动,比原来所预想的要复杂得多。20世纪60年代的研究结果是结构化编程(structuredprogramming)的出现,用规定的方法编写程序比非结构化编程能产生更清晰、更容易测试/调试以及更容易修改的程序。本书的第2章将介绍结构化编程原理。第3章到第5章则会开发多种结构化程序。
结构化编程研究的一个更具体结果是1971年NiklausWirth教授推出了Pascal语言。Pascal语言是用17世纪著名数学家和哲学家巴雷斯·帕斯卡(BlaisePascal)的名字命名的,常用于教学中讲解结构化编程.因而很快成为了大学中受欢迎的语言。但是这个语言缺乏在商业、工业和政府应用程序中所需要的许多特性,因此没有被大学以外的环境所接受。
Ada语言是在20世纪70年代和80年代初由美国国防部资助开发的。在此之前,国防部的导弹命令与控制软件系统是由几百种不同语言生成的,国防部要求用一种语言来完成大多数工作。Ada以Pascal为基础.但最终结构与Pascal大相径庭。这个语言是根据著名诗人LordByron的女儿(Ada Lovelace)的名字命名的。AdaLovelace在19世纪初编写了世界上第一个计算机程序,用于charlesBabbage设计的分析机引擎的计算设备。Ada的一个最重要功能是多任务(multiasking).程序员可以使多个活动任务并行发生。我们要介绍的其他常用高级语言(包括C/C++)通常只让程序员编写一次只有一个活动任务的程序。

1.12典型C++环境基础
C++系统通常由几个部分组成:程序开发环境、语言和C++标准库。下面介绍图1.1所示的典型C++环境。
C++程序通常要经过6个阶段(如图1.1),即编辑(edit)、预处理(Preprocess)、编译(compile)、连接(Link)、装入(load)和执行(excute)。这里主要介绍典型UNIXC++系统(注意,本书的程序不经修改或稍作修改即可在大多数当前的++系统中运行,包括MicrosoftWindows系统)。如果当前使用的不是UNIX系统,可以参看系统手册或向老师请教如何在相应环境中完成这些工作。
第一个阶段是编辑文件,这是用编辑器程序(editorprogram)完成的。程序员用编辑器输入C++程序,并进行必要的修改.然后将程序存放在磁盘之类的辅助存储设备中。C++程序文件名通常以.cpp、cxx或.C的扩展名结尾(注意C为大写),详见C++环境文档中对文件名扩展的说明。UNIX系统中两个广泛使用的编辑器是vi和emacs。个人计算机上的BorlandC++和MicrosoftvisualC++等C++软件包都有自己的编辑器,它们与编程环境紧密集成。这里,我们假设读者已经知道如何编辑程序。
随后的阶段是程序员发出编译(compile)程序的命令。编译器将C++程序翻译为机器语言代码(也称为目标码)。在C++系统中,预处理程序在编辑器翻译阶段开始之前自动执行。C++预处理器采用预处理指令(Preprocessdirective)表示程序编译之前要进行的某些操作。这些操作通常包括在要编译的文件中包括其他文本文件和进行各种文本替换。前面几章将介绍最常见的预处理指令,所有预处理指令的详细介绍见第17章“预处理器”。编辑器在将程序翻译为机器语言代码之前调用预处理器。
下一个阶段是连接。C++程序常常引用其他地方定义的函数,如标准库中或持定项目的程序员使用的专用库。C++编译器产生的目标码通常包含由于缺少一些内容而造成的“空穴”,连接器(1inker)将目标码与这些默认功能的代码连接起来,建立执行程序映像(不再缺少任何代码)。在典型的UNIX系统中,编译和连接C++程序的命令是CC。要编译和连接程序welcom.C,在UNIX提示符下键人:
CCwelcome.C
并按Enter键(或Return键)。如果程序编译和连接正确,则产生文件a.out。这就是welcome.C的执行程序映像。
再下一个阶段是装入。执行之前,要先把程序放进内存中,这是由装入器(Loader)完成的,装入器读取磁盘中执行程序的映象文件,并将其放进内存中。
最后,计算机在CPU控制下逐条指令地执行程序。要在UNIX系统中装入并执行程序.可在UNIX提示符下键入a.out并按Return键。
程序不是一次就能够运行。上述每个阶段都可能因为各种错误而失败。例如,可能除数为0(计算机上的非法操作,和算术运算中一样),这会使计算机打印出错信息。然后.程序员需返回编辑阶段,进行必要的修改并继续其余阶段,确定修改之后能否顺利工作。

常见编程错误1.1
如果除数为0之类的错误在程序运行时发生,则称这类错误为运行时错误(run-time error)或执行时错误(execution-timeerror)。除数为0通常是个致命错误,会使程序立即终止,无法完成工作。非致命错误能让程序运行完毕,但会产生错误结果(注意,在有些系统中.除数为0不是致命借误,详见系统的说明文档)。
C++中的大多数程序都要输入/输出数据。有些C++函数的输入来自cin(标准输人流),通常是键盘,但也可以连接其他输入设备。数据通常输出到co皿(标准输出流).一般是计算机屏幕,但也可以是其他设备。程序打印结果,通常是指在屏幕上显示结果。数据也可以输出到其他设备,如磁盘和硬拷贝打印机。还有称为cerr的标准错误流(standarderrorstream),通常连接屏幕,用于显示错误消息。用户经常把普通输出数据(即cout)路由到非屏幕设备,而让cerr输出到屏幕,可以立即通知用户发生错误。

1.13C++与本书的一般说明
C++是个复杂的语言。熟练的C++程序员有时以能够编写一些稀奇古怪的小程序为荣,这可不是好的编程习惯,因为这样会使程序更难阅读、更难测试和调试,也很难根据需求改变而改变。本书面向初学的程序员,因此我们强调情晰性。下面是第一个编程技巧。
编程技巧1.1
C++程序应以简单和直接的方式编写,称为KIS(“keep it simple,保持简单),不要使用不常用的方法任意地扩大程序。
本书提供了许多编程技巧,帮助读者养成良好习惯,编写更清晰、更易懂、更易维护、更易测试和调试的代码。这些技巧只是个指导原则,读者完全可以选择自己喜欢的编程风格。我们还介绍常见编程错误(在程序中要注意各种问题以避免这些错误)、性能提示(使程序的运行速度更快和使用更少内存的方法)、可移植性提示(帮助编写不经修改或稍作修改即可在其他计算机上运行的程序)、软件工程视点(影响和提高软件系统总体结构的概念和思想,特别是大型软件系统)和测试与调试提示(测试程序以找出和消除缺陷的提示)。
前面曾介绍过,C和C++是可移植语言,C和C++编写的程序可以在许多不同计算机上运行。可移植性是个重要目标。ANSI C语言标准文档(见参考文献An90)中列出了大量移植性问题,还有介绍移植性问题的专著(见参考文献Ja89和Ra90)。
可移植提示1.3
尽管可以编写可移植程序,但不同的C和C++编泽器和不同计算机的许多问题使移植性难以实现。C或C++写成的程序并不一定是可移植程序。程序员通常需要立接涉及不同的编泽嚣和不同的计算机。
前面介绍了ANSI/ISOC++草案标准文档的演变过程和检查了其完整性与准确性。但C++是个丰富的语言,其中有些细节和高级课题是我们所没有介绍的。如果需要C++的详细技术信息,可以阅读这个文档的最新草案。从下列web站点可以取得草案:
http://www.cygnus.com/m13c/wP/
我们列出了关于C++和面向对象编程的大量文献与图书目录,还列出了C++资源目录,包含许多与C++和面向对象编程的有关Web站点。
当前C++版本的特性与旧版本的不兼容,因此本文中的某些程序也许无法在旧版的C++编译器中工作。
编程技巧1.2
阅读所使用的C++版本的手册,经常翻阅这些手册,能够知道C++的丰富特性并正确使用这些特性。
编程技巧1.3
使用计算机和编泽器有助于学习C++。如果阅读所用C++版本的手册之后还不知道C++工作的特性,可以试用一个小的则试程序(testprogram),看看其如何工作。设置编泽器这项为最大警告(maximumwarning)。注意编译程序时出现的每个消息,并纠正问题,消除这些消息。

1.14C++编程简介
C++语言提供了计算机程序设计的结构化和规则化方法。我们现在要介绍C++编程,并用几个例子演示C++的许多重要特性.每个例子一次分析一条语句。第2章介绍C++中结构化编程的详细处理,然后到第5章一直使用结构化编程方法。第6章开始介绍C++面向对象编程,由于面向对象编程在本书的核心重要性,因此前五章各有一节“有关对象的思考”。这些小节介绍面向对象编程的概念和实例,让读者设计和实现面向对象的C++程序。

1.15 简单程序
C++使用非程序员可能感到奇怪的符号。我们首先介绍一个简单程序:打印一行文本。程序及其屏输出如图1.2。
这段程序演示了C++语言的几个重要特性。我们详细介绍程序的每一行。
//Fig.1.2:fig1_02.cpp
// A first program inC++
以//开头,表示该选项其余部分是注释语句(comment)。程序员手稿注释语句用来说明和提高程序的可读性。注释语句还可以帮助其它人阅读和理解程序。在运行程序时注释语句并不使计算机采用任何操作。C++编译器忽略注释误句,不产生任何机器目标码。注释语句"firstprogram in C++"只是描述该程序的用途。以//开头的说明语句称为单行貹注释语句(singned-linecomment),因为该注释语句在行尾结束(注意:C++程序员也可以用C语言注释语句样式,即注释语句以/开头,以/结束)。
1 // Fig. 1.2:fig01_02.cpp
2 // A firstprogram in C++
3#include<iostream.h>
4
5 int main()
6{
7 cout<<"Welcom toC++!\n";
8
9 rerturn 0; // indicate thatprogram ended sucessfully
10 }
输出结果:
Welcom to C++!

编程技巧1.4
每段程序以注释语句开头,描述该语句的用途。

下列语句:
#include<iostream.h>
是条预处理指令(preprocessordirective),是发给C++预处理器的消息。预处理器先处理以#开头的一行语句之后再编译该程序。为一行预处理指令告诉预处理器要在程序中包括输入、输出泫头文件iostream.h的内容。应该在任何使用C++式输入、输出泫从键盘输入数据或向屏幕输出数据的程序中包括这个头文件。图1.2中的程序向屏幕输出数据,iostream.h的内容将在稍后详细介绍。注意,最新ANSI、ISOC++杂标准实际上指定iostream.h和其它标准头文件不需要后缀.h,如iostream。我们在本书余下部分继续使用旧式头文件,因为许多编译器还不支持最新的ANSI/ISOC++草案标准。1.20节将再次介绍这个例子,演示如何使用新式头文件。

常见编程错误1.2
如果从键盘输入数据或向屏幕输出数据的程序中没有包括iostream.h头文件,则编译器会发出一个错误信息。
下列语句:
intmain()
是每个C++程序都包的语句。main后面的括号表示main是个程序基本组件,称为函数(function)。C++程序包含一个或几个函数,其中有且只有一个main函数即使main不是程序中的第一个函数,C++程序通常都从main函数开始执行。main左边的关键字int表示main返回一个整数值。我们将在第3章深入介绍函数时再介绍返回值的含义。目前只要在每个程序的main函数的左边包括关键字int即可。
左花括号({)应放在每个函数体(body)开头,对应右花括号(})应放在每个函数的结尾。下列语句:
cout<<"Welcom to C++!\n";
让计算机在屏幕上打印引号之间的字符串(string)。整个行称为语句(statement),包括cout<<运算符、字衔串"Welcom toC++!\n"和分号(;)。每条语句应以分号(又称为语句终止符)结束。C++中的输出和输入是用字符流(stream)完成的,这样,执行上述语句时,将字符流"Welcomto C++!"发送到标准输出流对象(standard output streamobject)cout,通常cout将其输出到屏幕。第11章“C++输入/输出流”中将详细介绍cout。
运算符<<称为流插入符(strem insertionoperator)。执行这个程序时,运算符右边的值(历操作数)插入输出流中。历操作数通常按引号中的原样直接打印。但注意字符\n不在屏幕中打印。反斜杠()称为转义符(escapcharacter),表示要输出特殊字符。字符串中遇到反斜杠时,下一个字符与反斜杠组合,形成转义序列(escapesequence)。转义序列\n表示换行符(newline)。使光标(即当前屏幕位置的指示符)移到下一行开头。表1.3列出了常用转义序列。
常见编程错误1.3
省略语句末尾的分号是个语法错误,语法错误编译器无法识别一个语句。编译器通常会发出错误消息,帮助程序员找到和纠正错误 。语法错误即违反了语言规则。语法错误也称为编译错误,因为它们在编译阶段出现。
下列语句:
return0; // indicate that programended sucessfully
放在每个main函数的末尾。C++的关键字return是退出函数的几种方式之一。main函数末尾使用return语句时,数值0表示顺利结束。第3章将详细介绍和解释包括这个语句的原因。目前只要记住在每个程序中都要包括这个语句。否则在某些程序中编译器会产生警告消息。
右花括号(})表示main函数结束。
------------------------------------------------------------------------------
转义序列说明
------------------------------------------------------------------------------
\n换行符,使屏幕光标移到屏幕中下一行开头
\t水平制表符,使屏幕光标移到下一制表位
\r回车符,使屏幕光标移到当前行开头,不移到下一行
\a警告,发出系统警告声音
\反斜杠,打印反斜杠符
"双引号,打印双引号
-------------------------------------------------------------------------------
图1.3 常用转义序列

编程技巧1.5
许多程序员让函数打印的最后一个字符为换行符(\n)。这样可以保证函数使屏幕光标移到屏幕中下一行开头,这种习惯能促进软件复用,这是软件开发环境中的关键目标。
编程技巧1.6
将每个函数的整个函数体在定义函数体的花括号中缩排一级,可使程序的函数结构更明显.使程序更易读。
编程技巧1.7
确定一个喜欢的缩排长度,然后一直坚持这个缩排长度。可以用制表符生成缩排,但制表位可能改变。建议用1/4英寸制表位或三个空格的缩排长度。
"Welcom toC++!"可用多种方法打印。例如,图1.4的程序用多条流插入语句,产生的程序输出与图1.2相同,因为每条流插人语句在上一条语句停止的位置开始打印。第一个流插入语句打印“Welcome”和空格,第二条流插入语句打印同一行空格后面的内容。C++允许以多种方式表达语句。
一条语句也可以用换行符打印多行,如图1.5。每次在输出流中遇到\n转义序列时,屏幕光标穆到下一行开头。要在输出中得到空行,只要将两个\n放在一起即可,如图1.5。
1 // Fig. 1.4:fig01_04.cpp
2// printing a line with multple statements
3#include<iostream.h>
4
5int main()
6 {
7 cout<<"Welcom ";
8 cout<<"to C++!\n";
9
10 return0;
11 }
输出结果:
Welcom toC++!

图1.4 用多条流插入语句打印一行

1 // Fig.1.5:fig01_05.cpp
2 // printing multiple lines with asingle statement
3#include<iostream.h>
4
5int main()
6 {
7cout<<"Welcom\nto\n\nC++!\n";
8
9 return0; // indicate that programended sucessfully
10 }

输出结果:
Welcome
to

C++!
图1.5 用一条流插入语句打印多行

1.16 简单程序:两个整数相加
下一个程序用输入流对象cin和流读取运算符>>取得用户从键盘中输入的两个整,计算这两个值的和,并将结果用cout输出。程序及其输出如图1.6。

1 // Fig.1.6:fig01_06.cpp
2 // Additionprogram
3#include<iostream.h>
4
5int main()
6 {
7 intinteger1,integer2,sum; //声明三个变量
8
9 cout<<"Enter firstinteger\n"; // 提示信息
10cin>>integer1;// 从键盘读一个整数
11cout<<"Enter second integer\n";//提示信息
12cin>>integer2;// 从键盘读一个整数
13sum=integer1+integer2;// 两整数相加,值赋给变量sum
14cout<<"Sum is"<<sum<<endl;//输出和
15
16return 0; //返回0值表示程序运行成功。
17 }

输出结果:
Enter first integer
45
Enter second integer
72
Sum is117

图1.6 两个整数相加
注释语句:
// Fig. 1.6:fig01_06.cpp
// Additionprogram
指定文件名和用途。C++预处理指令:
#include<iostream.h>
将iostream.h头文件的内容放进程序中。
前面介绍过,每个程序从main函数开始执行。左花括号表示main函数体开头,相应右花括号表示main函数体结束。下列语句:
int integer1,integer2,sum;
是个声明(declaration)。integer1,integer2和sum是变量(variable)名。变量是计算机内存中的地址,存放程序使用的值。这个声明指定变量integer1,integer2和sum的数据类型为int,表示这些变量保存整数值,如7、-11、0、31914。所有变量都应先声明名称和数据类型后才能在程序中使用。几个相同类型的变量可以在同一声明或多个声明中声明。我们可以一次只声明一个变量,但一次声明多个同类型变量更加简练。
编程技巧1.8
有些程序员喜欢一行只声明一个变量,这样可以在每个声明后面插入注释语句。

稍后要介绍数据类型float,(定义实数,即带小数点的数,如3.4、0.0、—11.19)和char(定义字符型数据.变量char只能保存一个小写字母、一个大写字母、一个数字或一个特殊字符,如x、$、7、*等等)。
编程技巧1.9
在每个逗号(,)后面加上空格,使程序更易读。
变量名是任何有效标识符(identifier)。标识符是一系列由字母、数字和下划线(_)组成的字符串,不能以数字开头。C++是区分大小写的,因此a1和A1是不同的标识符。

可移植教提示1.4
C++允许任意长度的标识符,但系统和C++版本可能限制标识符的长度不超过31个字符,以保证移植性。

编程技巧1.10
选择有意义的变量名能使程序更清楚,只要阅读程序就可以比较容易理解程序,而不必阅读手册或使用其它注释语句。

编程技巧1.11
避免用下划线和双下划线开头的标识符,因为C++内部使用这类名称。这样可以防止与编译器选择的名称冲突。

变量声明可以放在函数的任何位置,但变量声明必须放在程序使用变量之前。例如在图1.6所示的程序中,如果不用一条语句声明三个变量也可以分别声明。下列声明:
int integer1;
可以放在下列语句之前:
cln>>integerl;
下列声明:
intinteger2;
可以放在下列语句之前:
cin >>integer2;
下列声明:
intsum;
可以放在下列语句之前:
sum = integer1 +integer2;

编程技巧巧1.12
可执行语句之间的声明之前最好留一行空格,这样能使声明更明显,程序更清晰。

编程技巧1.13
如果喜欢把声明放在函数开头,应在这些声明的结尾与该函数中的执行语句开始之间留一行空格,将这些声明与该函数中的执行语句分开。
下事句:
cout<<"Enter firstinteger\n";
在屏幕上打印字符串Enter first integer(b也称为字符串直接量(stringliteral)或直接量(literal)),将光标移到下一行开头。这个消息称为提示(prompt),提示用户进行特定操作。上述语句表示cout得到字符串“Enterfirst integer\n".
下列语句:
cin>>integer1;
用输入流对象cin和流读取运算符>>取得键盘中的值。利用流读取运算符cin从标准输入流读取输入(通常是键盘输入)。上述语句表示cin提供integer1的值。
计算机执行上述语句时,等待用户输入变量integer1的值。用户输入整数值并按Enter键(或Return键),将数值发送给计算机。然后计算机将这个数(值)赋给变量integer1。程序中后面引用integer1时都使用这个值。
cout和cin流对象实现用户与计算机之间的交互。由于这个交互像对话一样,因此通常称为对话式计算(conversationalcomputing)或交互式计算(interactive computing)。
下列语句:
cout<<"Enter secondinteger\n";
在屏幕上打印”Enter secondinteger"字样,然后移到下一行的开头。这个语句提示用户进行操作。下列语句:
cin>>integer2;
从用户取得变量integer2的值。
赋值语句:
sum=integer1+integer2;
计算变量integer1和integer2的和,然后用赋值运算符(assignmentoperator)"="将结果赋给变量sum。这个语句表示sum取得integer1加integer2的值。大多数计算都是在赋值语句中进行的。“=”运算符和前面的“+”运算符称为二元运算符,两个操作数是integer1和integer2。而对于“=”运算符,两个操作数是sum和表达式integer1+integer2的值。

编程技巧 1.14
二元运算符两边要放上空格,这样能使运算符更明显,程序更易读。

下列语句:
cout<<"Sumis"<<sum<<endl;
打印字符串"Sum is"和变量sum的数值,加上称为流操纵算子的endl(endline的缩写)。endl输出一个换行符,然后刷新输出缓冲区,即在一些系统中,输出暂时在机器中缓存,等缓冲区满时再打印到屏幕上,endl强制立即输出到屏幕上。
注意,上述语句输出多种不同类的值,流插入运算符知道如何输出每个数据。在一个语句中使用多个流插入运算符称为连接(concatenating)、链接(chaining)或连续使用流插入操作。这样,就不必用多条输出语句输出多个数据。
计算可以在输出语句中进行。可以将上述语句合二为一:
cout<<"Sumis"<<integer1+integer2<<endl;
从而不需要变量sum。
右花括号告诉计算机到达了函数main的结尾。
C++的一个强大我就是用户可以生成自己的数据类型(详见第6章),然后可以告诉C++如何用>>和<<运算符输入或输出这种类型的值(称为运算符重载,见第8章)。

1.17 内存的概念
integer1、integer2和sum等变量名实际上对应于计算机内存中的地址(location)。每个变量都有名称(name)、类型(type)、长度(size)和值(value)。
在图1.6所示的加法程序中,执行下列语句时:
cin>>integer1;
用户输入的值放在C++编译器为integer1指事实上的内存地址中。假设用户输入integer1的值45,则计算机将45放在地址integer1中,如图1.7。
integer1
45
图1.7内存中的值

无论何时将新值放入内存地址,这个值将取代该地址中原有的值,并删除前一个值。
运行前面提到的加法程序,当执行下中句时:
cin>>integer2;
假设用户输入舋,则计算机将72放在地址integer2中,如图1.8。注意编译器不一定将这两个地址指定为内存中相邻地址。
程序取得integer1和integer2值后,它将这两个值相加,并将和放在变量sum中。下列语句:
sum=integer1+integer2;
进行加法运算,同时也删除一个值,即把integer1和integer2的和放进地址sum中时,sum原有的值丢失。计算sum之后,内存如图1.9。注意integer1和integer2的值和计算前一样,虽然这些值在计算机进行计算时使用,但并不删除。因此,从一个内存地址读取数值时,这个过程是非破坏性的。
integer1
45
integer2
72
图1.8 两个变量值之后的内存值
integer1
45 integer2
72sum
117
图1.9计算之后的内存值

1.18算术运算
大多数都要进行算术运算。算术运算符见图1.10,注意这里使用了许多代数中没有使用的符号。星号()表示乘法、百分号(%)表示求模(modulus)将在稍后介绍。图1.10所示的算术运算符都是二元运算符,即这些运算符取两个操作数。例如,表达式"integer1+integer2"包含二元运算符“+”和两个操作数integer1和integer2。

C++操作算术运算符代数表达式C++表达式 加+f+7f+7
减-p-cp-c
bmbm
除/x/y或x÷yx/y
求模%r modsr%s
图1.10 算术运算符
整除(即除数和被除数均为整数)取得整数结果。例如,表达式7/4得1,表达式17/5得3。注意,整除结果忽略分数部分,不用取整。
C++提供求模(modulus)运算符“%”即求得整除的余数。求模运算是个整数运算符,只能使用整数操作数。表达式x%y取得x除以y的余数,这样,7%4得3,17%5得2。后面几章将介绍求模运算符许多有趣的应用。如确定一个数是否为另一个数的倍数(确定一个数为奇数或偶数是这个问题的一个特例)。

常见编程错误 1.4
对非整型操作数使用求模运算衔是个语法错误。
C++中的算术运算表达式应以直线形式在计算机中输入。这样,a除以b应输入为"a/b",使所有常量、变量和运算符放在一行中。编译器通常不接受下列代数符号:
a

b
但有些特殊专业软件包支持复杂数学表达式更自然的表示方法。
C++表达式中括号的使用和代数表达式中相同。例如,要将a乘以b+c的和,可以用:
a
(b+c)
C++中算术运算符的运算顺序是由运算符的优先级规则确定的,与代数中的相同:
1.括号中的表达式先求值,程序员可以用括号指定运算顺序。括号具有最高优先级,对于嵌套括号,由内存向外层求值。
2.乘法、除法、求模运算优先。如果表达式中有多个乘法、除法、求模运算,则从左向右求值。乘法、除法、求模的优先级相同。
3.然后再进行加法和减法。如果表达式中有多个加法和减法,则从左向右求值。加法和减法的优先级相同。

运算符优先级保证C++按正确顺序采用运算符。从左向右求值指的是运算符的结合律(associativity),也有一些运算符结合律是从右向左。图1.11总结了运算符优先级规则,引入其它C++运算符时,这个表可以扩充,详细的运算符优先级请参见附录。

运算符运算求值顺序()括号最先求值,如果有嵌套括号,则先求最内层表达式的值,如果
同一层有几对括号,则从左向右求值。
、/、或%乘、除、求模其次求值。如果有多个,则从左向右求值。
+或-加、减最后求值。如要有多个,则从左向右求值。

图 1.11算术运算符优先级

下面用几个表达式说明运算符优先级规则。每个例子都列出代数表达式和对应的C+表达式。
下例求五个值的算术平均值:
a+b+c+d+e
代数: m =------------
5
C++: m =(a+b+c+d+e)/5;

括号是必须的,因为作法的优先级比加法高,要把整个和(a+b+c+d+e)除以5,如果不加括号,则a+b+c+d+e/5的取值为:
a+b+c+d+(e/5)
下例是直线的方程:
代数: y = mx+b
C++: y =m
x+b;
不需要括号,乘法优先于加法,因此先乘后加。
下列包含模(%)、乘、除、加、减运算:
代数: z =pr%q+w/x-y
C++: z = p * r % q + w / x -y;
⑥ ① ② ④③⑤
语句下面的圆圈数字表示C++采用运算符的顺序。乘法、求模和除法首先从左向右求值(结合律为从左向右)因为它们的优先级高于加法和减法。然后进行加法和减法运算,也是从左向右求值。
并不是有多对括号的表达式都包含嵌套括号。例如下列表达式不包含嵌套括号:
a (b+c)+c(d+e)
这些括号在同一层。
要更好地了解运算符优先级规则,考虑二次多项式的求值:
y = a * x * x + b * x +c;
⑥ ① ② ④③⑤
语句下面的圆圈数字表示C++采用运算符的顺序。C++中没有指数运算符,因此我们把x2表示为xx,稍后会介绍标准库函数pow(数)。由于pow所需要的数据类型有一些特殊情况,因此放第3章再介绍。
假设变量a、b、c、x初始化如下:a=2,b=3,c=7和x=5。图1.12演示了上述二次多项式的运算符优先级。



上述赋值语句可以加上多余的括号,使代码更清晰:
y=(a
xx)+(bx)+c;

编程技巧1.15
和代数中一样,可以在表达式中加上多余的括号,使代码更清晰,这些括号称为冗余括号。冗余括号常用于组合表达式中的子表达式,使表达式更加清晰。

1.19 判断:相等与关系运算符
本节介绍简单的C++if结构,使程序根据某些条件的真假做出判断。如果条件符合,即为真(true),则执行if结构体的语句;如果不符合,即条件为假(false),则不执行语句,稍后将举例说明。
if结构中的条件可以用相等运算符(equality operator)和关系运算符(relational operator)表示,如图1.13关系运算符具有相同的优先级,结合律为从左向右。相等运算符的优先级也相同,但低于关系运算符的优先级,结合律也为从左向右。

标准代数相等与关系运算符C++相等与关系运算符 C++条件举例C++条件含义 ===x==yx等于y
≠!=x!=yx不等于y
关系运算符
>>x>yx大于y
<<x<yx小于y
≥>=x>=yx大于或等于y
≤<=x<=yx小于或等于y


图 1.13 相等与关系运算符

常见编程错误1.5:
如果==、!=、>=和<=运算符的符号对之间出现空格,则会出现语法错误。
常见编程错误1.6:
逆转!=、>=和<=运算符的符号顺序(变为=!、=>、和=<)通常会出现语法错误。将!=写成=!有时不会出现语法错误,但会出现逻辑错误。
常见编程错误1.7
不要把相等运算符==与赋值运算符=混淆起来。相等运算符表示等于,而赋值运算符表示取、取值或赋值。有人把相等运算符读作双等于。稍后会介绍,把相等运算符==与赋值运算符混淆起来可能不会造成明显的语法错误,但可能造成相当明显的逻辑错误。

下例用六个if语句比较用户输入的两个数。如果其中任何一个if语句的条件成立,则执行与该if相关联的输出语句。图1.14显示了这个程序和三个示例输出。
注意图1.14的程序边疆使用流读取操作输入两个整数。首先将一个值读到num1中,然后将一个值读到num2中。if语句的缩排是为了提高程序的可读性。另外,注意图1.14中每个if语句体中有一条语句。第2章将会介绍结构体中有多条语句的if语句(将语句体放在花括号“{ }”中)。

1 //Fig.1.14:fig01_14.cpp
2 // Using ifstatements,relationnal
3 // operators,andequality operators
4#include<iosream.h>
5
6int main()
7 {
8 int num1,num2;
9
10 cout<<"Enter twointegers,and I will tell you\n"
11 <<"the relationships they satisfy:";
12cin>>num1>>num2; //读取两个整数
13
14if(num1==num2)
15cout<<num1<<" is equalto"<<num2<<endl;
16
17if(num1!=num2)
18cout<<num1<<" is not equal to"num2<<endl;
19
20 if(num1<num2)
21cout<<num1<<" is less than"<<num2<<endl;
22
23 if(num1>num2)
24cout<<num1<<" is greater than"<<num2<<endl;
25
26 if(num1<=num2)
27cout<<num1<<" is less than or equal to "
28<<num2<<endl;
29
30 if(num1>=num2)
31cout<<num1<<" is greater than or equal to"
32<<num2<<endl;
33
34 return0; //返回一个程序正常结束的标识
35 }

输出结果:
Enter two integers,and I will tllyou
The relationships they satisfy: 37
3 is not equal 7
3 is lessthan 7
3 is less than or equal to7

Enter two integers,and I will tellyou
the relationships they satisfy:2212
22 is not equal 12
22 isgretaer than 12
22 is greater than or equal to12
Enter two integers,and I will tellyou
the relationships they satisfy:77
7 is equal to 7
7 is lessthan or equal to 7
7 is greater than or equal to7

图1.14 使用相等和关系运算符
编程技巧1.16
if语句中的缩排可以提高程序的可读性,突出结构体
编程技巧1.17
程序中一行只放一条语句

常见编程错误1.8
if语句的打件后面的右括号之后紧跟着分号通常是个逻辑错误(但不是语法错误)。分号使if结构体变成空的,不管打件是否为真,这个if结构都不进行任何操作。更糟糕的是,原先if结构体变成if结构后面的语句,不管条件如何总是执行,通常会使程序产生错误结果。

注意图1.14中空格的用法。在C++语言中,空白字符(如制表符、换行符和空格)通常都被编译器忽略。因此,语句中可以根据程序员的爱好加上换行符和空格,但不能用换行符和空格分隔标识符。
觉编程错误1.9
把main写成main是语法错误。

编程技巧1.18
长语句可以分成多行。如果一条语句要分成多行,可以在分隔列表的逗号或长表达式的运算符后面断行。如果语句要分成两行或多行,后续行可以编排。

图1.15显示了本章介绍的运算符优先级,运算符优先顺序从上向下递减。注意赋值运算符之后的所有运算符结合律均为从左向右。加法是左结合的,因此表达式x+y+z求值为(x+y)+z。赋值运算符是从右向左结合的,因此表达式x=y=0求值为x=(y=0),首先将0赋给y,然后将结果0赋给x。

运算符结合律类型 ()从左向右括号
* /%从左向右乘
+-从左向右加
<<>>从左向右流插入/读取
< <=>>=从左向右关系
==!=从左向右等于
=从右向左赋值


图 1.15 运算符优先级和结合律

编程技巧1.19
编写包含多个运算符的表达式时要参考运算符优先级,确保表达式中的运算符按所要求的运算。如果无法确定复杂表达式中的求值顺序,可以用括号强制顺序,就像代数式中一样。注意,有些运算符(如赋值运算符)是从右向左结合的,而不是从左向右。

前面介绍了C++的许多特性,包括在屏幕上打印数据,从键盘输入数据、进行计算和作出判断。第2章利用这些知识介绍结构化编和,将会学到更多的编程技巧,介绍如何指定和改变语句执行顺序(称为控制流)。

1.20新型头文件与名字空间
本节是为使用支持ANSI/ISO草案标准的编译器用户提供的。草案标准指定了许多旧式C++头文件的新名,包括iostream.h,大多数新式头文件不再用扩展名.h。图1.16改写图1.2,演示新型头文件和两种使用标准库头文件的方法。
第3行:
#include<iostream>
演示新型头文件 名语法。
第5行:
usingnamespacestd;
指定用std名字空间(namespace),这是C++中的新特性。名字空间可以帮助程序员开发新的软件组件而不会与现有软件组件产生命名冲突。开发类库的一个问题是类和函数名可能已经使用。名字空间能为每个新软件组件保持惟一的名称。
1 // Fig. 1.16:fig01_16.cpp
2// Using new-style header files
3#include<iostream.h>
4
5using namespace std;
6
7 intmain()
8 {
9 cout<<"Welcom toC++!\n";
10std::cout<<"Welwcom to C++!\n";
11
12 return0;
13 }

输出结果:
Welcom toC++!
Welcom toC++!

图 1.16 使用新型头文件

C++草案标准中的每个头文件用名字空间std保证今后C++标准库操作的每个特性是惟一的,不会与其它程序员开发的软件组件混淆起来,程序员不能用名字空间定义新的类库。上述语句只是表示我们使用C++标准库中的软件组件,要定义自己的类库,则可以将我们的所有类和函数放在名字空间deitel中,使我们的类库与所有其它公司的类库和C++标准类库区别开来。
程序中出现"using namespacestd"语句之后,就可以像第9行那样用cout对象将数值输出到标准输出流中。如果使用两个或几个类库,其中有的特性名称相同,则可能引起名称冲突。这时就要用名字空间来完全限定所用的名称,例如第10行的std::cout:
std::cout<<"Welcom toC++!\n";
cout的完全限定名为std::cout,如果全部采用这种形式,虽然比较繁琐,但程序中第5行的"usingnamespacestd"语句就没有必要了。using语句可以在C++标准库中使用每个名称的简写版本(或其它指定名字空间的名称)。我们将在本书稍后详细介绍名字空间。目前并不是所有C++环境都已经支持新的头文件的命名格式。为此,我们在本书大部分地方使用旧式头文件,只在介绍C++标准新特性时才使用新的头文件命名格式,使用新格式时将特别注明。

1.21 有关对象的思考
下面要开始介绍面向对象。面向对象是观察世界和编写计算机程序的自然方式。
那么,为什么不从一开始就介绍面向对象呢?我们为什么要把面向对象放到第6章再介绍呢?原因是我们要建立的对象是由各个结构化程序段组成的,因此先要介绍结构化编程基础。
前面五章先介绍结构化编程的传统方法。然后在每一章的最后介绍面向对象。第一章要介绍基本概念(如“有关对象的思考”)和术语(如“对象表达”)。第2章到第5章考虑更实质的问题,然后用面向对象设计(object-orienteddesign,OOD)技术攻克难题。我们将分析建立系统时的典型问题语句,确定实现系统需要哪些对象、对象需要哪些属性、表示哪些行为以指定对象之间如何交互,以满足系统的需要。我们用实际问题而不是课堂例子来介绍这些内容,而且是在编写面向对象的C++程序之前介绍。第6章就可以开始在C++中编写面向对象的系统。
我们首先介绍一些面向对象的关键术语。看看实际生活中,对象无处不在,人、动物、植物、汽车、飞机、大楼、计算机都是对象,我们通过对象来思考问题。我们有较高的抽象(abstrction)能力。可以将屏幕图形作为人、飞机、树、山等对象,而不是各个颜