第1单元 Hello,C++!
本单元教学目标介绍C++程序的基本结构以及在计算机上输入、编译、调试和运行C++程序的基本方法和步骤。
学习要求了解C++程序的基本特点,熟悉Visual C++集成开发环境的基本使用方法。
授课内容软件开发与C++语言我们知道,使用计算机工作是通过相应的应用软件进行的,如用于文字处理的Word,科学计算的MATLAB,表格处理的EXCEL和各种数据库软件等,这些软件均由专业软件开发人员设计编程。一般来说,日常工作中遇到的任务大多数均可借助现成的应用软件完成,但有时仍需为具体问题自行开发相应的软件。特别是在工程应用领域中,解决各类项目中可能遇到的大量具体问题,使用通用的软件不仅效率低下,还可能无法完成任务。在这种情况下,自行编制具有针对性的相应软件可能是唯一的解决方法。
为计算机编写软件需要使用程序设计语言。目前可用的计算机语言很多,各有其特点。有些适用于开发数据库应用程序,有些适用于开发科学计算程序,有的简便易学,有的功能全面。在本课程中,我们介绍Visual C++语言。
Visual C++语言支持面向对象的程序设计方法,适用于WINDOWS应用程序的开发,可用于开发各类应用程序,功能十分强大,是目前最流行的程序设计语言之一。本教程以面向对象的程序设计方法为中心,本着简明、实用和循序渐进的原则,介绍使用Visual C++开发应用程序的基本方法。
算法与程序要编写用于解决应用问题的程序,首先必需确定解决问题的方案,也就是算法。例如,对于问题,给定两个正整数p和q,如何求出其最大公因数? 古希腊数学家欧几里德 (Euclid) 给出了一个著名的算法:
步骤1,如果p < q,交换p 和q;
步骤2,求p/q的余数r;
步骤3,如果r = 0,则 q 就是所求的结果;
否则反复做如下工作:
令p = q,q = r,重新计算 p 和q 的余数r,直到r = 0为止则 q 就是原来的两正整数的最大公因数,
从原则上说,有了算法,人们就可以借助纸、笔和算盘等工具直接求解问题了。但如果问题比较复杂,计算步骤很多,则应通过编程,使用计算机解决。
[例1-1] 使用欧几里德算法,编写一程序求解任意两正整数的最大公因数。
说 明:根据1.6.3:“用Developer Studio编写和调试简单C++程序”建立项目并调试和运行。
程 序:
// Example 1-1,计算两个正整数的最大公因数
#include <iostream.h>
void main()
{ // 声明三个整型变量 p,q,r
int p,q,r;
// 提示用户由键盘输入两个正整数
cout<< "Please input two integer numbers:" << endl;
cin >> p >> q;
// 如果 p < q,交换 p 和 q
if(p<q)
{ r = p;
p = q;
q = r;
}
// 计算 p 除 q 的余数 r
r = p%q;
// 只要 r 不等于 0,重复进行下列计算
while(r != 0)
{ p = q;
q = r;
r = p%q;
}
// 输出结果
cout << "The maximum common divisor is " << q <<,.” << endl;
}
输入数据: 输入两个整数,其间以空格分隔。如
12 18
输出数据:The maximum common divisor is 6.
分 析,可以看出,该程序的主体部分几乎与原算法完全相同,只是增加了一些C++语言特有的内容。
程序的第1行是注解。注解以“//”开头,直到该行的末尾,用于说明或解释程序段的功能、变量的作用以及程序员认为应该向程序阅读者说明的任何内容。可以看到,在该程序中还有一些注解,用于说明程序的结构。在将C++程序编译成目标代码时所有的注解行都会被忽略掉,因此即使使用了很多注解也不会影响目标码的效率。恰当地应用注解可以使程序清晰易懂、便于调试,便于程序员之间的交流与协作,因此在自己编写的每个程序中都使用精心撰写的注解是一个良好的编程习惯。C++中还有一种注解格式,可参看1.5.2。
第2行是编译预处理,有关内容将在1.5.5节介绍。
从第3行到最后一行,是主函数。主函数是该程序的主体部分,由其声明部分
void main()
和用一对花括号{}括起来的函数体构成。在函数体内,除了注解行以外,还有语句。C++的语句可分为声明语句和执行语句,声明语句用于声明程序中使用的变量、函数等的类型和参数,执行语句实际执行某功能。第5行就是一个类型声明语句
int p,q,r;
声明在该程序中使用了3个整型变量。关于类型和变量等概念,将在第3单元中详细介绍。
就一般的计算机程序而言,总是要包括三个基本内容:数据输入、计算和输出。
该程序的输入部分首先在计算机屏幕上显示一行提示信息(第7行),
Please input two integer numbers:
然后等待使用者由键盘输入两个整数。用户输入的两个正整数由语句
cin >> p >> q;
分别存入变量p和q。
程序下面的部分就是欧几里德算法的具体实现了。可以看出,C++程序类似自然语言,只是结构更加严谨,对照前面的算法不难理解。
程序中的最后一个语句是输出语句,将计算结果显示在计算机屏幕上。
输入、编译、调试和运行一个C++程序
C++是一种编译语言,C++源程序需要经过编译、连接,生成可执行文件后方可运行。使用C++开发一个应用程序大致要经过以下步骤:
首先要根据实际问题确定编程的思路,包括选用适当的数学模型。这方面的内容其实也是各应用学科的主要研究领域之一;
根据前述思路或数学模型编写程序。除了非常简单的问题可以直接写出相应的C++程序之外(在值得使用计算机解决的应用问题中这种情况并不多),一般都应该采用第2单元中介绍的“逐步求精”的结构化程序设计方法来编程;
编辑源程序。首先应将源程序输入计算机,这项工作可以通过任何一种文本编辑器(如Visual C++集成环境中的文本编辑器)完成。输入的源程序一般以文件的形式存放在磁盘上(后缀为CPP)。
编译和连接。在设计高级语言(包括C++)时充分考虑了人(程序员)的需要,源程序很接近人类自然语言。因此,需要将源程序转换为计算机可直接执行的指令。就C++而言,这项工作又可分为称为编译和连接的两个步骤,编译阶段将源程序转换成目标文件(后缀为OBJ),连接阶段将目标文件连接成可执行文件(后缀为EXE)。
反复上机调试程序,直到改正了所有的编译错误和运行错误。在调试过程中应该精心选择典型数据进行试算,避免因调试数据不能反映实际数据的特征而引起计算偏差和运行错误;
运行。如果是自用程序,在调试通过以后即可使用实际数据运行程序,得到计算结果;如果是商品软件或受委托开发的软件,则运行由用户实施。
应该说明的是,如果要利用C++ 开发一个大型应用系统,例如管理信息系统、数据库应用系统、计算机辅助教学系统或者实时控制系统等,一般要比编写一个数值计算方面的应用程序复杂得多,上面介绍的开发步骤就显得过于简单了。这时要遵循软件工程的方法进行应用系统开发,例如采用面向对象的设计开发技术。这方面的内容已经超出本课程的范围,有兴趣的读者可以参看有关软件工程方面的书籍和资料。
自学内容
1.4 C++语言的历史、特点、用途和发展早期出现的高级程序设计语言大都面向某个具体的应用领域,如用于科学计算的FORTRAN和Algol60、用于商业数据处理的 COBOL以及用于人工智能编程的LISP等等。但是对于最基本的系统软件,如操作系统和各种高级语言的编译程序来说,却缺乏一种合适的高级程序设计语言,因此通常还是直接使用机器指令代码或者汇编语言编写程序,效率很低且易于出错,根本无法适应规模越来越大的系统软件开发。究其原因,是当时的计算机硬件的基本性能,如处理速度、存储容量等相对来说还不很高,因此系统软件的主导设计思想是充分发挥系统硬件的能力,尽量提高软件的运行效率;而当时现有的程序设计语言与计算机硬件的实际情况脱节,经编译后生成的目标代码冗余量大、运行速度慢,也不适于用来编写作为应用软件的基础的操作系统、编译程序等系统软件。
70年代初,随着半导体集成电路技术的发展,计算机硬件的性能有了很大的提高,出现了每秒运算次数达百万次以上的大型计算机。为这种计算机配备的各种软件的规模也越来越大,结构越来越复杂,其生产组织和管理机制与极端注重代码运行效率的、以程序员个体劳动为基础的传统编程方式之间产生了尖锐的矛盾,终于导致了所谓的“软件危机”。其直接结果是出现了程序设计思想的革命,一改重视发挥机器效率的旧程序设计方法为重视人的因素,强调充分发挥程序员的效率,强调程序员之间合作交流的能力,即以提高程序可读性为主要目标的结构化程序设计方法。
C语言正是在这种时代背景下诞生的。
1969年,美国贝尔实验室的Ken Thompson为 DEC PDP-7计算机设计了一个操作系统软件,这就是最早的UNIX。接着,他又根据剑桥大学的 Martin Richards设计的BCPL语言为UNIX设计了一种便于编写系统软件的语言,命名为B。B语言是一种无类型的语言,直接对机器字操作,这一点和后来的C语言有很大不同。作为系统软件编程语言的第一个应用,ken Thompson使用B语言重写了其自身的解释程序。
1972-1973年间,同在贝尔实验室的Denis Ritchie改造了B语言,为其添加了数据类型的概念,并将原来的解释程序改写为可以直接生成机器代码的编译程序,然后将其命名为C。1973年,Ken Thompson小组在 PDP-11机上用C重新改写了UNIX的内核。与此同时,C语言的编译程序也被移植到IBM 360/370、Honeywell 11 以及VAX-11/780等多种计算机上,迅速成为应用最广泛的系统程序设计语言。
然而,C语言也存在一些缺陷,例如类型检查机制相对较弱;缺少支持代码重用的语言结构等,造成用C语言开发大程序比较困难。
为了克服C语言存在的缺点,并保持C语言简洁、高效的特点,贝尔实验室的 Bjarne Stroustrup博士及其同事开始对C语言进行改进和扩充,将“类”的概念引入了C语言,构成了最早的C++语言(1983)。后来,Stroustrup 和他的同事们又为C++引进了运算符重载、引用、虚函数等许多特性,并使之更加精炼,于1989年推出了AT&T C++2.0版。随后美国标准化协会以AT&T C++2.0为基础,制定了ANSI C++标准。各软件商推出的C++编译器都支持该标准,并有不同程度的拓展。
C++支持面向对象的程序设计方法(参看第7单元),特别适合于中型和大型的软件开发项目,从开发时间、费用到软件的可重用性、可扩充性、可维护性和可靠性等方面,C++均具有很大的优越性。同时,C++又是C语言的一个超集,这就使得许多C代码不经修改就可被C++编译器编译通过。
早期的C++编译器称为Cfront,实际上只是一个预处理程序,负责将C++程序转换为C程序,然后再由C语言编程序继续编译。后来也出现了可以直接将C++程序编译为目标代码的C++编译程序。
随着Windows 操作系统的出现,应用程序的外观和结构发生了巨大的变化,程序设计的环境也得到了很大的改善。为了适应Windows编程,各软件厂商纷纷推出了新型C++编译器,Microsoft公司的Visual C++就是其中的矫矫者。Visual C++并不是一个单纯的编译器,而是一整套用于软件开发的集成环境(IDE),其中包括了文本编辑、编译连接、调试、可视化界面设计和完备的在线帮助,甚至可直接通过Internet与Microsoft公司的站点连接,以得到技术支持和产品更新升级。
1.5 C++程序的基本要素
1.5.1 标识符、关键词和标点符号标识符是程序中变量、类型、函数和标号的名称。标识符由字母、数字和下划线“_”组成,第一个字符不能是数字。与FORTRAN和BASIC等程序设计语言不同,在C++的编译器把大写和小写字母当作不同的字符。这个特征称为“大小写敏感的”。各种C++编译器对在标识符中最多可以使用多少个字符的规定各不相同,ANSI标准规定编译器应识别标识符的前6个字符,而Visual C++编译器允许在程序中使用长达247个字符的标识符!在标识符中恰当运用下划线,大、小写字母混用以及使用较长的名字都有助于提高程序的可读性。例如在一个人事管理软件中,用函数名read_employee_file ( ) (读职工档案)、变量名EmployeeCount (职工人数) 等就要比用函数名f ( )、变量名n等清楚得多。
在Visual C++中,有下列关键词:
asm,auto,bad_cast,bad_typed,bool,break,case,catch,char,class,const,const_cast,continue,default,delete,do,double,dynamic_cast,else,enum,except,extern,explicit,false,finally,float,for,friend,goto,if,inline,int,long,mutable,namespace,new,operator,private,protected,public,register,reinterpret_cast,return,short,signed,sizeof,static,static_cast,struct,switch,template,this,throw,try,type_info,typedef,typeid,union,unsigned,using,virtual,void,volatile,while,xalloc
用于表示C++本身的特定成份,具有相应的语义。程序员在命名变量、数组和函数的名称时,不能使用这些标识符。
另外,C++还使用了下列12个标识符作为编译预处理的命令单词:
define,elif,else,endif,error,if,ifdef,ifndef,include,line,progma,undef
并赋予了特定含义。程序员在命名变量、数组和函数时也不要使用它们。
在C++字符集中标点和特殊字符有各种用途,从组织程序文本到定义编译器或编译的程序的执行功能。有些标点符号也是运算符 (参见第3单元“基本数据类型与表达式”中的有关内容),编译器可从上下文确定它们的用途。C++的标点有
[ ] ( ) { } * &,,= ;,.,#
这些字符在C++中均具有特定含义。
1.5.2 注解
C++的注解有两种形式,一种以两个斜杠符“//”起头,直至行末。一种是用斜线星号组合,/*”和“*/”括起的任意文字(但不能再包含“/*”和“*/”,即注解不能嵌套),多用于注解篇幅多于一行的情况。注解可以出现在空白字符允许出现的任何地方,编译器把注解作为一个空白字符处理。
恰当使用注解可以使程序容易阅读。
1.5.3 源程序一份C++源程序由一个或多个源代码文件构成。C++源程序中包括命令、编译指示、声明、定义、注解和函数等内容。为了使程序的结构清晰,通常的做法是在一个源代码文件中放置变量、类型、宏和类等的声明(称为头文件,后缀为.H),然后在另一个源代码文件中引用这些变量(称为源程序文件,后缀为.CPP)。采用这种方式编写的程序,很容易查找和修改各类声明。
1.5.4 编译预处理将程序编译的过程分为预处理和正式编译两个步骤是C++的一大特点。在编译C++程序时,编译器中的预处理模块首先根据预处理命令对源程序进行适当的加工,然后才进行正式编译。
预处理命令均以符号“#”开头,且在一行中只能书写一条预处理命令 (过长的预处理命令可以在使用续行标志“\”后续写在下一行上),且结束时不能使用语句结束符(分号“;”)。
C++中有3种主要编译预处理命令:宏定义、文件包含和条件编译。下面先介绍其中的前两种,条件编译将在第4单元中介绍。
宏定义无参宏定义通常用来定义符号常数,其格式为:
#define <宏名> <替换序列>
其中<宏名>是一个标识符。为了和变量有所区别,习惯上在为无参宏起名时只使用大写字母。例如:
#define PI 3.14159
该宏定义命令为常量3.14159起了一个符号化的名字PI。这样,在该宏定义命令之后的程序中,均可以使用符号常数PI表示数值3.14159,例如:
s = PI*r*r;
使用符号常数的程序可读性好,易于阅读理解。另外,将程序中的常量用符号常数表示也有利于程序的调试和修改。例如,程序中分别使用了两个符号常数,它们的值碰巧相同:
#define MAX_NUMBER 80
#define LINE_LEN 80
以后如果发现需要将其中的一个修改为其他数值,则只须修改上面的宏定义即可。但是如果不使用宏定义,则需逐个查出程序中所有数值80,一一判断是否是需要修改的那一个。不但费事,而且容易出错。
应该说明的是用宏命令定义的符号常数不是变量,象
LINE_LEN = 100;
这样的用法是错误的。其实,宏定义命令的真正含义是要求编译程序在对源程序进行预处理时,将源程序中所有的符号名“MAX_NUMBER”和“LINE_LEN” (但不包括出现在注解与字符串中的“MAX_NUMBER”和“LINE_LEN”) 分别替换为字符序列“80”。因此到了正式编译时,符号名“LINE_LEN”已经不存在了,语句“LINE_LEN = 100;”已经变为“80 = 100;”,由于赋值运算符的左边不是变量而是一个常数80,显然这是一个错误的赋值表达式语句。
在定义宏时还可以加上参数,这就构成了带参数的宏:
#define <宏名>(<参数表>) <带有参数的替换序列>
例如
#define max(a,b) ((a)>(b)?(a):(b))
带参数的宏的使用用法颇象函数。例如:
x = max(x,10);
即如果变量x的值小于10,则将常数10赋给x,否则x的值保持不变。实际上,这个带参数的宏的确切含义为,通知编译程序中的预处理模块在对应用程序进行处理时遇到形如max()的串时要进行转换,在转换时还要对参数进行代换处理。上述宏定义经过转换后会变为
x = ((x)>(10)?(x):(10));
在利用宏命令定义带参数的宏时,要注意宏名与括号之间不能有空格,且所有的参数均应出现于右边的替换序列中。
虽然带参数的宏很象函数,但这两者之间的区别是很明显的。编译程序在处理带参数的宏时,并不是用实参的值进行代入计算,而只是简单地将替换序列中的参数用相应的实参原样替换 (按参数书写位置,从左到右一一对应)。因此,在书写带参数的宏时,要防止由于使用表达式参数带来的错误。例如定义了一个用于计算圆面积的宏:
#define circle_area(r) r*r*3.14159
在计算
s = circle_area(x+16)
就会出问题。将参数x+16代入上述宏定义中,有
s = x+16*x+16*3.14159
就会发现运算的顺序显然有错误。如果重新定义这个宏:
#define circle_area(r) ((r)*(r)*3.14159)
就没有问题了。总而言之,在定义带参数的宏时应将每个参数和整个替换序列都用括号括起来,以防止可能的计算错误[注1]。
取消宏定义命令#undef用于撤消对一个符号名的定义。例如
#define DEBUG 1
// 在本程序段落中定义了一个值为1的符号常数DEBUG
#undef DEBUG
如果没有#undef命令,则宏定义起作用的范围为自#define命令起至该源程序文件末尾。
文件包含编译预处理命令中的文件包含命令的功能是将另一段C++源程序文件嵌入到正在进行预处理的源程序中的相应位置上。文件包含命令的格式为:
#include <文件名>
或者
#include "文件名"
其中“文件名”是指被嵌入的C++源程序文件的文件名,必须用双引号或者尖括号(实际上是一个小于号和一个大于号)括起来。通过使用不同的括号可以通知预处理程序在查找嵌入文件时采用不同的策略。如果使用了尖括号,那么预处理程序在系统规定的目录(通常是在系统的include子目录)中查找该文件。如果使用双引号,那么编译预处理程序首先在当前目录中查找嵌入文件,如果找不到则再去由操作系统的path命令所设置的各个目录中去查找。如果仍然没有查找到,最后再去上述规定的目录(include子目录)中查找。
原来的源程序文件和用文件包含命令嵌入的源程序文件在逻辑上被看成是同一个文件,经过编译后生成一个目标文件,如图1-1所示。
1.5.5 输入与输出程序通常会要求用户提供一些信息(如数据),这一过程被称为程序的输入。程序通常总是要向用户发出一些信息(如计算结果等),这一过程被称为程序的输出。C++程序的输入操作可由cin对象来完成,而输出操作则可由cout对象来完成。
cin的基本用法为:
cin>>V1>>V2>>…>>Vn;
其中“>>”称做提取运算符,V1,V2,…,Vn都是变量。这个语句的意思是,程序暂时中止执行,等待用户从键盘上输入数据。用户输入了所有的数据后,应以回车键表示输入结束,程序将用户键入的数据存入各变量中,并继续执行下面的语句。例如例1-1中的第8行:
cin >> p >> q;
就是要求用户分别为变量p和q各输入一个数据。在输入时,应注意用空格或tab键将所输入的数据分隔开。
应说明的是,当用户响应cin的要求输入数据时,必须注意所输入数据的类型(C++的数据类型将在第3单元中介绍)应与接受该数据之变量的类型相匹配,否则输入操作将会失败或者得到的将是一个错误的数据。
cout的基本用法具有如下的一般形式:
cout << E1 << E2 <<…<< Em;
其中“<<”称做插入运算符,E1,E2,…,Em都是表达式(第3单元中介绍)。这个语句的意思是,将各表达式的值输出(显示)到屏幕上当前光标位置处。在输出时,要注意恰当使用字符串和新行符endl,提高输出信息的可读性。
调试技术
1.6 Visual C++的集成开发环境
Visual C++软件包包含了许多独立的组件,如编辑器、编译器、链接器、实用程序生成器、调试器,以及各种各样为开发Windows环境下的C/C++程序而设计的工具。其中最重要的是一个名为Developer Studio的集成开发环境。Developer Studio把所有的Visual C++工具结合在一起,集成为一个由窗口、对话框、菜单、工具栏、快捷键及宏组成的和谐系统,通过该集成环境,程序员可以观察和控制整个开发进程。
图1-2显示了一个典型的Developer Studio主窗口。
从图1-2中可以看出,Developer Studio主窗口可以分为几个部分:窗口顶部是菜单和工具栏,左面的一个子窗口是工作区窗口,工作区的右面是编辑子窗口。最下面是输出子窗口。值得注意的是,上述各种部件,包括子窗口、菜单栏和工具栏的位置不是一成不变的,可以根据个人的喜好重新安排。
1.6.1 菜单和工具栏
Developer Studio中有一个Menu Bar(菜单栏,通常停靠在开发环境窗口的顶部),其中的菜单项有File (文件处理)、Edit(编辑功能)、View(查看)、Insert(插入)、Project(项目管理)、Build(编译)、Tools(工具)、Window(窗口)和Help(帮助)等,分别对应一个下拉子菜单。
除菜单栏外,开发环境中还有几个工具栏,一般均放在开发环境的顶部,菜单栏的下方,如Standard(标准工具栏,用于文件管理、编辑和查看等),Wizard Bar(向导工具栏)和Build MiniBar(建立工具栏,用于编译、连接等)。工具栏上有常用命令的图标。一般来说,工具栏上的命令在菜单中均有对应选项,但工具栏使用更方便,只要用鼠标左键点击工具栏中的相应图标即可调用相应的功能。
开发环境的各种菜单栏和工具栏均为停靠式,可以用鼠标拖动改变它们的位置,也可使用菜单选项Tools/Customize…(表示菜单栏的Tools子菜单中的Customize…选项,下同)中的Toolbars选项卡打开或关闭相应的工具栏,或修改工具栏的内容。
除此之外,Developer Studio的所有部分几乎都可响应鼠标右键单击而弹出一个上下文相关菜单。甚至当Developer Studio没有打开窗口时,在空白区右击鼠标也会弹出一个菜单,其中含有使窗口可见和调整工具栏是否可见的命令。在工具栏上除标题栏外的任何地方右击鼠标,同样可以弹出菜单。在使用集成环境工作时试一试鼠标右键,还会发现许多其他的快捷方式。例如,通过在工具栏或菜单栏上不是按钮或菜单名的地方单击并保持鼠标按键被按下,就可以把它们拖动到屏幕上新的地方。
1.6.2 Developer Studio窗口除了各种对话框外,Developer Studio显示两种类型的窗口,即文档窗口和停靠窗口。文档窗口是一般的带边框子窗口,其中含有源代码文本或图形文档。Window子菜单中列出了在屏幕上以平铺方式还是以层叠方式显示文档窗口的命令。所有其他的Developer Studio窗口,包括工具栏和菜单栏,都是停靠式窗口。开发环境有两个主要的停靠窗口――Workspace(工作区)窗口和Output(输出)窗口。另外还有一个Debugger(调试器)停靠窗口,只在调试过程中显示。
停靠窗口可以固定在Developer Studio用户区的顶端、底端或侧面,或者浮动在屏幕上任何地方。停靠窗口,不论是浮动着的或是固定着的,总是出现在文档窗口的上面。这样,就保证了当焦点从一个窗口移到另一个时,浮动的工具栏一直都是可见的。但这也意味着,文档窗口偶尔会看起来像消失了似的。例如,如果你正在文本编辑器中编辑源代码,此时打开一个占据整个Developer Studio用户区的停靠窗口,源代码文档就会消失,它隐藏在新窗口之下。解决方法是要么关了覆盖的窗口,要么把它拖到不挡眼的地方去。
1.6.3 用Developer Studio编写和调试简单C++程序下面以例1-1为例,说明使用Visual 集成开发环境编写和调试简单C++程序的步骤。
首先打开Developer Studio,选择菜单选项File/New中的Projects(项目)选项卡。从卡中选择Win32 Console Application(32位控制台应用程序)。
为你的应用项目取一名字填写在选项卡右上方的Project Name(项目名称)处,并检查下面的Location(位置)中列出的路径是否正确。注意使下面的单选框选择Create New Workspace(即要建立新工作区),然后按下“OK”键。
这时会出现Application Wizard(应用程序生成向导),提问要生成的项目类型。选择“An Empty Project(空项目)”,按下Finish(结束)键,会弹出一窗口,显示新项目的有关信息。检查无误后按“OK”键。
这时屏上会显示一个空项目,并生成一个工作区文件(后缀为.DSW)。再次选择File菜单的New选项,并选择Files选项卡。从卡中选择C++ Source File(C++源程序)。
为你的程序取一名字(可与项目名相同)填写在选项卡右边的File处,然后按下“OK”键。
这时开发环境右侧的文件编辑窗口中出现了一个空文件,即可输入源程序的内容。
程序输入后,应仔细检查一遍,然后就可以编译了。
选择菜单选项Build/Compile (((.cpp (其中(((为文件名)编译源程序。稍候片刻,会在Output窗口(通常在屏幕下方)中显示编译结果,如出错信息等(编译错误的处理在下一单元介绍)。如果程序正确,编译结果会生成一个目标文件(后缀为.OBJ)。
目标文件还需通过连接才能生成可执行文件。选择菜单选项Build/Build (((.exe连接目标文件。连接的结果同样显示在Output窗口中。连接的结果为可执行文件(后缀为.EXE)。
以上两步也可以并为一步,即直接使用菜单选项Build/Build (((.exe。
生成的可执行文件可以单独运行,也可以在开发环境中直接运行,后一种方法在程序需要反复调试时更加方便。在集成开发环境中直接运行程序的方法是选用菜单选项Build/Excute (((.exe。
1.6.4 菜单选项、快捷键和工具栏
Developer Studio的许多功能都有不只一种调用方法。例如,执行一个编译、连接成功的可执行文件,既可通过选择菜单选项Build/Execute (((.exe完成,也可以直接使用快捷键Ctrl-F5,还可以用鼠标点击Build MiniBar(编译工具栏)上的相应图标来完成。一般来说,越是常用的功能,可以调用的方法越多、越方便。
初学者应特别注意的是,通常一个项目只对应一份程序。因此,在结束一个程序之后,应退出当前项目(使用Developer Studio的菜单选项File/Close Workspace),然后再为新程序重建一个新的项目。
程序设计举例
[例1-2]计算太阳和地球之间的万有引力。
算法分析,由普通物理知,两个质量分别为m1和m2的物体之间的万有引力F与两个物体质量的乘积成正比,与两个物体质心之间的距离R的平方成反比:
式中的G为引力恒量,其具体数值与式中各量的量纲有关。如果取质量的单位为克,距离的单位为厘米,力的单位为达因,则
G ≈6.666667×10-8厘米 /克·秒因此,只要将太阳的质量1.987×1033克和地球的质量5.975×1027克以及两者之间的距离1.495×1013厘米代入上式,即可算出太阳和地球之间的万有引力。
程 序
// Example 1-2:计算太阳和地球之间的万有引力
#include <iostream.h>
double grav(double m1,double m2,double distance)
{
double g,G = 6.66667E-8;
g = G*m1*m2/(distance*distance);
return g;
}
void main()
{
double g,Msun = 1.987E33,Mearth = 5.975E27;
g = grav(Msun,Mearth,1.495E13);
cout << "The gravitation between sun and earth is "<<g<<" Dyne." << endl;
}
输 出,The gravitation between sun and earth is 3.5413E+27 Dyne.
分 析,在设计该程序时,我们将计算任意两个质点之间的引力公式单独编写为一个函数grav(),然后在主函数中调用该函数具体计算太阳和地球之间的万有引力。这样做有两个好处:
(1)首先,简化了主函数的编写。在编写主函数时,可以认为已经有了一个用于计算万有引力的现成函数grav(),我们只需要按要求填写好参数后调用该函数就可以得到计算结果,和调用标准的库函数方法完全相同。这样即使grav()由其他程序员编写,我们在不必了解其内部结构的情况下也可以方便地调用之;
(2)如果以后还要计算其他物体之间的引力,例如地球和月球之间的万有引力,就不必再次编写相应的程序段了,只需在调用grav()函数时换上相应的参数即可。
按这种方法设计程序就称为模块化程序设计,是结构化程序设计方法(我们将在第二单元中详细介绍结构化程序设计方法)的一部分。
[例1-3] 加法计算器程序。
程 序:
// Example 1-3:加法计算器程序
#include <iostream.h>
void main()
{
double a,b,c;
cout<<"Please input two numbers,";
cin>>a>>b;
c = a+b;
cout << a << " + " << b << " = " << c<< endl;
}
输 入,Please input two numbers,12.0 34.0
输 出,12 + 34 = 46
分 析,本例使用了双精度浮点类型的变量进行运算,所以可以计算小数加法。程序在接收输入数据之前首先显示一行提示信息,告诉用户应该如何输入数据,并在输出结果时同时输出了计算公式。这些作法都是为了方便程序的用户,是编写应用程序的基本要求。
[例1-4] 显示生日卡。该程序首先要求输入收信人和发信人的姓名,然后在屏幕上显示出完整的生日卡来。
程 序:
// Example 1-4:显示生日卡
#include <iostream.h>
void main()
{
char name1[41],name2[41];
cout<< endl << "Please input your friend's name,";
cin >> name1;
cout<< endl << "Please input your name,";
cin >> name2;
cout<< endl << "====================================" << endl;
cout<< "My dearest " << name1 << "," << endl;
cout<< " Happy birthday to you!" << endl;
cout<< " yours," << endl;
cout<< " " << name2 << endl;
cout<< "====================================" << endl;
}
输 入,Please input your friend's name,Zhang
Please input your name,Li
输 出,====================================
My dearest Zhang,
Happy birthday to you!
yours,
Li
====================================
分 析,该程序可以通过输入收信人和发信人的姓名来构造相应的生日卡。注意程序中使用了字符类型的数组存放表示收信人和发信人名字的字符串,数组中的每个元素用于存放一个字母。
单元上机练习题目
1,在计算机上通过运行本单元的各例题,熟悉Developer Studio的使用方法。
2,乘法计算器程序:请同学们根据例1-3自行改编。
3,修改例1-4的生日卡程序,使其能够输入和显示日期。
4,使用梯形法计算定积分,其中a = 0,b = 1,被积函数为sin(x),取积分区间等分数为 1000。
计算原理和方法,将积分区间等分为n份,其中第i个小区间上的定积分
可以使用如图1-3所示的梯形的面积来近似。
因此整个积分区间上的定积分可表示为
程 序:
// Example 1-5:用梯形法计算定积分
#include <iostream.h>
#include <math.h>
// 定义被积函数
double f(double x)
{
return sin(x);
}
// 主函数,用梯形法计算定积分
void main()
{
double a,b; // 双精度类型变量,积分的下限和上限
double h; // 双精度类型变量,积分步长
double sum; // 双精度类型变量,工作变量,最后为积分值
int n; // 整型变量,积分区间等分数
int i; // 整型变量,循环工作变量
// 根据题意确定积分的下限、上限和积分区间等分数
a = 0.0;
b = 1.0;
n = 1000;
h = (b-a)/n; // 计算小区间长度
// 为工作变量赋初值,先计算不易循环运算的部分
sum = (f(a)+f(b))/2;
// 循环计算公式中的Σ和式
for(i=1;i<n;i=i+1)
sum = sum+f(a+i*h);
// 完成计算,变量 sum 中存放积分结果
sum = sum*h;
// 输出计算结果
cout<<"The result is " << sum << endl;
}
5.改编上题的程序,使之可以用来计算定积分:
积分区域等分数可取为200,并将以上两题的计算结果和手算结果相比较。
本单元教学目标介绍C++程序的基本结构以及在计算机上输入、编译、调试和运行C++程序的基本方法和步骤。
学习要求了解C++程序的基本特点,熟悉Visual C++集成开发环境的基本使用方法。
授课内容软件开发与C++语言我们知道,使用计算机工作是通过相应的应用软件进行的,如用于文字处理的Word,科学计算的MATLAB,表格处理的EXCEL和各种数据库软件等,这些软件均由专业软件开发人员设计编程。一般来说,日常工作中遇到的任务大多数均可借助现成的应用软件完成,但有时仍需为具体问题自行开发相应的软件。特别是在工程应用领域中,解决各类项目中可能遇到的大量具体问题,使用通用的软件不仅效率低下,还可能无法完成任务。在这种情况下,自行编制具有针对性的相应软件可能是唯一的解决方法。
为计算机编写软件需要使用程序设计语言。目前可用的计算机语言很多,各有其特点。有些适用于开发数据库应用程序,有些适用于开发科学计算程序,有的简便易学,有的功能全面。在本课程中,我们介绍Visual C++语言。
Visual C++语言支持面向对象的程序设计方法,适用于WINDOWS应用程序的开发,可用于开发各类应用程序,功能十分强大,是目前最流行的程序设计语言之一。本教程以面向对象的程序设计方法为中心,本着简明、实用和循序渐进的原则,介绍使用Visual C++开发应用程序的基本方法。
算法与程序要编写用于解决应用问题的程序,首先必需确定解决问题的方案,也就是算法。例如,对于问题,给定两个正整数p和q,如何求出其最大公因数? 古希腊数学家欧几里德 (Euclid) 给出了一个著名的算法:
步骤1,如果p < q,交换p 和q;
步骤2,求p/q的余数r;
步骤3,如果r = 0,则 q 就是所求的结果;
否则反复做如下工作:
令p = q,q = r,重新计算 p 和q 的余数r,直到r = 0为止则 q 就是原来的两正整数的最大公因数,
从原则上说,有了算法,人们就可以借助纸、笔和算盘等工具直接求解问题了。但如果问题比较复杂,计算步骤很多,则应通过编程,使用计算机解决。
[例1-1] 使用欧几里德算法,编写一程序求解任意两正整数的最大公因数。
说 明:根据1.6.3:“用Developer Studio编写和调试简单C++程序”建立项目并调试和运行。
程 序:
// Example 1-1,计算两个正整数的最大公因数
#include <iostream.h>
void main()
{ // 声明三个整型变量 p,q,r
int p,q,r;
// 提示用户由键盘输入两个正整数
cout<< "Please input two integer numbers:" << endl;
cin >> p >> q;
// 如果 p < q,交换 p 和 q
if(p<q)
{ r = p;
p = q;
q = r;
}
// 计算 p 除 q 的余数 r
r = p%q;
// 只要 r 不等于 0,重复进行下列计算
while(r != 0)
{ p = q;
q = r;
r = p%q;
}
// 输出结果
cout << "The maximum common divisor is " << q <<,.” << endl;
}
输入数据: 输入两个整数,其间以空格分隔。如
12 18
输出数据:The maximum common divisor is 6.
分 析,可以看出,该程序的主体部分几乎与原算法完全相同,只是增加了一些C++语言特有的内容。
程序的第1行是注解。注解以“//”开头,直到该行的末尾,用于说明或解释程序段的功能、变量的作用以及程序员认为应该向程序阅读者说明的任何内容。可以看到,在该程序中还有一些注解,用于说明程序的结构。在将C++程序编译成目标代码时所有的注解行都会被忽略掉,因此即使使用了很多注解也不会影响目标码的效率。恰当地应用注解可以使程序清晰易懂、便于调试,便于程序员之间的交流与协作,因此在自己编写的每个程序中都使用精心撰写的注解是一个良好的编程习惯。C++中还有一种注解格式,可参看1.5.2。
第2行是编译预处理,有关内容将在1.5.5节介绍。
从第3行到最后一行,是主函数。主函数是该程序的主体部分,由其声明部分
void main()
和用一对花括号{}括起来的函数体构成。在函数体内,除了注解行以外,还有语句。C++的语句可分为声明语句和执行语句,声明语句用于声明程序中使用的变量、函数等的类型和参数,执行语句实际执行某功能。第5行就是一个类型声明语句
int p,q,r;
声明在该程序中使用了3个整型变量。关于类型和变量等概念,将在第3单元中详细介绍。
就一般的计算机程序而言,总是要包括三个基本内容:数据输入、计算和输出。
该程序的输入部分首先在计算机屏幕上显示一行提示信息(第7行),
Please input two integer numbers:
然后等待使用者由键盘输入两个整数。用户输入的两个正整数由语句
cin >> p >> q;
分别存入变量p和q。
程序下面的部分就是欧几里德算法的具体实现了。可以看出,C++程序类似自然语言,只是结构更加严谨,对照前面的算法不难理解。
程序中的最后一个语句是输出语句,将计算结果显示在计算机屏幕上。
输入、编译、调试和运行一个C++程序
C++是一种编译语言,C++源程序需要经过编译、连接,生成可执行文件后方可运行。使用C++开发一个应用程序大致要经过以下步骤:
首先要根据实际问题确定编程的思路,包括选用适当的数学模型。这方面的内容其实也是各应用学科的主要研究领域之一;
根据前述思路或数学模型编写程序。除了非常简单的问题可以直接写出相应的C++程序之外(在值得使用计算机解决的应用问题中这种情况并不多),一般都应该采用第2单元中介绍的“逐步求精”的结构化程序设计方法来编程;
编辑源程序。首先应将源程序输入计算机,这项工作可以通过任何一种文本编辑器(如Visual C++集成环境中的文本编辑器)完成。输入的源程序一般以文件的形式存放在磁盘上(后缀为CPP)。
编译和连接。在设计高级语言(包括C++)时充分考虑了人(程序员)的需要,源程序很接近人类自然语言。因此,需要将源程序转换为计算机可直接执行的指令。就C++而言,这项工作又可分为称为编译和连接的两个步骤,编译阶段将源程序转换成目标文件(后缀为OBJ),连接阶段将目标文件连接成可执行文件(后缀为EXE)。
反复上机调试程序,直到改正了所有的编译错误和运行错误。在调试过程中应该精心选择典型数据进行试算,避免因调试数据不能反映实际数据的特征而引起计算偏差和运行错误;
运行。如果是自用程序,在调试通过以后即可使用实际数据运行程序,得到计算结果;如果是商品软件或受委托开发的软件,则运行由用户实施。
应该说明的是,如果要利用C++ 开发一个大型应用系统,例如管理信息系统、数据库应用系统、计算机辅助教学系统或者实时控制系统等,一般要比编写一个数值计算方面的应用程序复杂得多,上面介绍的开发步骤就显得过于简单了。这时要遵循软件工程的方法进行应用系统开发,例如采用面向对象的设计开发技术。这方面的内容已经超出本课程的范围,有兴趣的读者可以参看有关软件工程方面的书籍和资料。
自学内容
1.4 C++语言的历史、特点、用途和发展早期出现的高级程序设计语言大都面向某个具体的应用领域,如用于科学计算的FORTRAN和Algol60、用于商业数据处理的 COBOL以及用于人工智能编程的LISP等等。但是对于最基本的系统软件,如操作系统和各种高级语言的编译程序来说,却缺乏一种合适的高级程序设计语言,因此通常还是直接使用机器指令代码或者汇编语言编写程序,效率很低且易于出错,根本无法适应规模越来越大的系统软件开发。究其原因,是当时的计算机硬件的基本性能,如处理速度、存储容量等相对来说还不很高,因此系统软件的主导设计思想是充分发挥系统硬件的能力,尽量提高软件的运行效率;而当时现有的程序设计语言与计算机硬件的实际情况脱节,经编译后生成的目标代码冗余量大、运行速度慢,也不适于用来编写作为应用软件的基础的操作系统、编译程序等系统软件。
70年代初,随着半导体集成电路技术的发展,计算机硬件的性能有了很大的提高,出现了每秒运算次数达百万次以上的大型计算机。为这种计算机配备的各种软件的规模也越来越大,结构越来越复杂,其生产组织和管理机制与极端注重代码运行效率的、以程序员个体劳动为基础的传统编程方式之间产生了尖锐的矛盾,终于导致了所谓的“软件危机”。其直接结果是出现了程序设计思想的革命,一改重视发挥机器效率的旧程序设计方法为重视人的因素,强调充分发挥程序员的效率,强调程序员之间合作交流的能力,即以提高程序可读性为主要目标的结构化程序设计方法。
C语言正是在这种时代背景下诞生的。
1969年,美国贝尔实验室的Ken Thompson为 DEC PDP-7计算机设计了一个操作系统软件,这就是最早的UNIX。接着,他又根据剑桥大学的 Martin Richards设计的BCPL语言为UNIX设计了一种便于编写系统软件的语言,命名为B。B语言是一种无类型的语言,直接对机器字操作,这一点和后来的C语言有很大不同。作为系统软件编程语言的第一个应用,ken Thompson使用B语言重写了其自身的解释程序。
1972-1973年间,同在贝尔实验室的Denis Ritchie改造了B语言,为其添加了数据类型的概念,并将原来的解释程序改写为可以直接生成机器代码的编译程序,然后将其命名为C。1973年,Ken Thompson小组在 PDP-11机上用C重新改写了UNIX的内核。与此同时,C语言的编译程序也被移植到IBM 360/370、Honeywell 11 以及VAX-11/780等多种计算机上,迅速成为应用最广泛的系统程序设计语言。
然而,C语言也存在一些缺陷,例如类型检查机制相对较弱;缺少支持代码重用的语言结构等,造成用C语言开发大程序比较困难。
为了克服C语言存在的缺点,并保持C语言简洁、高效的特点,贝尔实验室的 Bjarne Stroustrup博士及其同事开始对C语言进行改进和扩充,将“类”的概念引入了C语言,构成了最早的C++语言(1983)。后来,Stroustrup 和他的同事们又为C++引进了运算符重载、引用、虚函数等许多特性,并使之更加精炼,于1989年推出了AT&T C++2.0版。随后美国标准化协会以AT&T C++2.0为基础,制定了ANSI C++标准。各软件商推出的C++编译器都支持该标准,并有不同程度的拓展。
C++支持面向对象的程序设计方法(参看第7单元),特别适合于中型和大型的软件开发项目,从开发时间、费用到软件的可重用性、可扩充性、可维护性和可靠性等方面,C++均具有很大的优越性。同时,C++又是C语言的一个超集,这就使得许多C代码不经修改就可被C++编译器编译通过。
早期的C++编译器称为Cfront,实际上只是一个预处理程序,负责将C++程序转换为C程序,然后再由C语言编程序继续编译。后来也出现了可以直接将C++程序编译为目标代码的C++编译程序。
随着Windows 操作系统的出现,应用程序的外观和结构发生了巨大的变化,程序设计的环境也得到了很大的改善。为了适应Windows编程,各软件厂商纷纷推出了新型C++编译器,Microsoft公司的Visual C++就是其中的矫矫者。Visual C++并不是一个单纯的编译器,而是一整套用于软件开发的集成环境(IDE),其中包括了文本编辑、编译连接、调试、可视化界面设计和完备的在线帮助,甚至可直接通过Internet与Microsoft公司的站点连接,以得到技术支持和产品更新升级。
1.5 C++程序的基本要素
1.5.1 标识符、关键词和标点符号标识符是程序中变量、类型、函数和标号的名称。标识符由字母、数字和下划线“_”组成,第一个字符不能是数字。与FORTRAN和BASIC等程序设计语言不同,在C++的编译器把大写和小写字母当作不同的字符。这个特征称为“大小写敏感的”。各种C++编译器对在标识符中最多可以使用多少个字符的规定各不相同,ANSI标准规定编译器应识别标识符的前6个字符,而Visual C++编译器允许在程序中使用长达247个字符的标识符!在标识符中恰当运用下划线,大、小写字母混用以及使用较长的名字都有助于提高程序的可读性。例如在一个人事管理软件中,用函数名read_employee_file ( ) (读职工档案)、变量名EmployeeCount (职工人数) 等就要比用函数名f ( )、变量名n等清楚得多。
在Visual C++中,有下列关键词:
asm,auto,bad_cast,bad_typed,bool,break,case,catch,char,class,const,const_cast,continue,default,delete,do,double,dynamic_cast,else,enum,except,extern,explicit,false,finally,float,for,friend,goto,if,inline,int,long,mutable,namespace,new,operator,private,protected,public,register,reinterpret_cast,return,short,signed,sizeof,static,static_cast,struct,switch,template,this,throw,try,type_info,typedef,typeid,union,unsigned,using,virtual,void,volatile,while,xalloc
用于表示C++本身的特定成份,具有相应的语义。程序员在命名变量、数组和函数的名称时,不能使用这些标识符。
另外,C++还使用了下列12个标识符作为编译预处理的命令单词:
define,elif,else,endif,error,if,ifdef,ifndef,include,line,progma,undef
并赋予了特定含义。程序员在命名变量、数组和函数时也不要使用它们。
在C++字符集中标点和特殊字符有各种用途,从组织程序文本到定义编译器或编译的程序的执行功能。有些标点符号也是运算符 (参见第3单元“基本数据类型与表达式”中的有关内容),编译器可从上下文确定它们的用途。C++的标点有
[ ] ( ) { } * &,,= ;,.,#
这些字符在C++中均具有特定含义。
1.5.2 注解
C++的注解有两种形式,一种以两个斜杠符“//”起头,直至行末。一种是用斜线星号组合,/*”和“*/”括起的任意文字(但不能再包含“/*”和“*/”,即注解不能嵌套),多用于注解篇幅多于一行的情况。注解可以出现在空白字符允许出现的任何地方,编译器把注解作为一个空白字符处理。
恰当使用注解可以使程序容易阅读。
1.5.3 源程序一份C++源程序由一个或多个源代码文件构成。C++源程序中包括命令、编译指示、声明、定义、注解和函数等内容。为了使程序的结构清晰,通常的做法是在一个源代码文件中放置变量、类型、宏和类等的声明(称为头文件,后缀为.H),然后在另一个源代码文件中引用这些变量(称为源程序文件,后缀为.CPP)。采用这种方式编写的程序,很容易查找和修改各类声明。
1.5.4 编译预处理将程序编译的过程分为预处理和正式编译两个步骤是C++的一大特点。在编译C++程序时,编译器中的预处理模块首先根据预处理命令对源程序进行适当的加工,然后才进行正式编译。
预处理命令均以符号“#”开头,且在一行中只能书写一条预处理命令 (过长的预处理命令可以在使用续行标志“\”后续写在下一行上),且结束时不能使用语句结束符(分号“;”)。
C++中有3种主要编译预处理命令:宏定义、文件包含和条件编译。下面先介绍其中的前两种,条件编译将在第4单元中介绍。
宏定义无参宏定义通常用来定义符号常数,其格式为:
#define <宏名> <替换序列>
其中<宏名>是一个标识符。为了和变量有所区别,习惯上在为无参宏起名时只使用大写字母。例如:
#define PI 3.14159
该宏定义命令为常量3.14159起了一个符号化的名字PI。这样,在该宏定义命令之后的程序中,均可以使用符号常数PI表示数值3.14159,例如:
s = PI*r*r;
使用符号常数的程序可读性好,易于阅读理解。另外,将程序中的常量用符号常数表示也有利于程序的调试和修改。例如,程序中分别使用了两个符号常数,它们的值碰巧相同:
#define MAX_NUMBER 80
#define LINE_LEN 80
以后如果发现需要将其中的一个修改为其他数值,则只须修改上面的宏定义即可。但是如果不使用宏定义,则需逐个查出程序中所有数值80,一一判断是否是需要修改的那一个。不但费事,而且容易出错。
应该说明的是用宏命令定义的符号常数不是变量,象
LINE_LEN = 100;
这样的用法是错误的。其实,宏定义命令的真正含义是要求编译程序在对源程序进行预处理时,将源程序中所有的符号名“MAX_NUMBER”和“LINE_LEN” (但不包括出现在注解与字符串中的“MAX_NUMBER”和“LINE_LEN”) 分别替换为字符序列“80”。因此到了正式编译时,符号名“LINE_LEN”已经不存在了,语句“LINE_LEN = 100;”已经变为“80 = 100;”,由于赋值运算符的左边不是变量而是一个常数80,显然这是一个错误的赋值表达式语句。
在定义宏时还可以加上参数,这就构成了带参数的宏:
#define <宏名>(<参数表>) <带有参数的替换序列>
例如
#define max(a,b) ((a)>(b)?(a):(b))
带参数的宏的使用用法颇象函数。例如:
x = max(x,10);
即如果变量x的值小于10,则将常数10赋给x,否则x的值保持不变。实际上,这个带参数的宏的确切含义为,通知编译程序中的预处理模块在对应用程序进行处理时遇到形如max()的串时要进行转换,在转换时还要对参数进行代换处理。上述宏定义经过转换后会变为
x = ((x)>(10)?(x):(10));
在利用宏命令定义带参数的宏时,要注意宏名与括号之间不能有空格,且所有的参数均应出现于右边的替换序列中。
虽然带参数的宏很象函数,但这两者之间的区别是很明显的。编译程序在处理带参数的宏时,并不是用实参的值进行代入计算,而只是简单地将替换序列中的参数用相应的实参原样替换 (按参数书写位置,从左到右一一对应)。因此,在书写带参数的宏时,要防止由于使用表达式参数带来的错误。例如定义了一个用于计算圆面积的宏:
#define circle_area(r) r*r*3.14159
在计算
s = circle_area(x+16)
就会出问题。将参数x+16代入上述宏定义中,有
s = x+16*x+16*3.14159
就会发现运算的顺序显然有错误。如果重新定义这个宏:
#define circle_area(r) ((r)*(r)*3.14159)
就没有问题了。总而言之,在定义带参数的宏时应将每个参数和整个替换序列都用括号括起来,以防止可能的计算错误[注1]。
取消宏定义命令#undef用于撤消对一个符号名的定义。例如
#define DEBUG 1
// 在本程序段落中定义了一个值为1的符号常数DEBUG
#undef DEBUG
如果没有#undef命令,则宏定义起作用的范围为自#define命令起至该源程序文件末尾。
文件包含编译预处理命令中的文件包含命令的功能是将另一段C++源程序文件嵌入到正在进行预处理的源程序中的相应位置上。文件包含命令的格式为:
#include <文件名>
或者
#include "文件名"
其中“文件名”是指被嵌入的C++源程序文件的文件名,必须用双引号或者尖括号(实际上是一个小于号和一个大于号)括起来。通过使用不同的括号可以通知预处理程序在查找嵌入文件时采用不同的策略。如果使用了尖括号,那么预处理程序在系统规定的目录(通常是在系统的include子目录)中查找该文件。如果使用双引号,那么编译预处理程序首先在当前目录中查找嵌入文件,如果找不到则再去由操作系统的path命令所设置的各个目录中去查找。如果仍然没有查找到,最后再去上述规定的目录(include子目录)中查找。
原来的源程序文件和用文件包含命令嵌入的源程序文件在逻辑上被看成是同一个文件,经过编译后生成一个目标文件,如图1-1所示。
1.5.5 输入与输出程序通常会要求用户提供一些信息(如数据),这一过程被称为程序的输入。程序通常总是要向用户发出一些信息(如计算结果等),这一过程被称为程序的输出。C++程序的输入操作可由cin对象来完成,而输出操作则可由cout对象来完成。
cin的基本用法为:
cin>>V1>>V2>>…>>Vn;
其中“>>”称做提取运算符,V1,V2,…,Vn都是变量。这个语句的意思是,程序暂时中止执行,等待用户从键盘上输入数据。用户输入了所有的数据后,应以回车键表示输入结束,程序将用户键入的数据存入各变量中,并继续执行下面的语句。例如例1-1中的第8行:
cin >> p >> q;
就是要求用户分别为变量p和q各输入一个数据。在输入时,应注意用空格或tab键将所输入的数据分隔开。
应说明的是,当用户响应cin的要求输入数据时,必须注意所输入数据的类型(C++的数据类型将在第3单元中介绍)应与接受该数据之变量的类型相匹配,否则输入操作将会失败或者得到的将是一个错误的数据。
cout的基本用法具有如下的一般形式:
cout << E1 << E2 <<…<< Em;
其中“<<”称做插入运算符,E1,E2,…,Em都是表达式(第3单元中介绍)。这个语句的意思是,将各表达式的值输出(显示)到屏幕上当前光标位置处。在输出时,要注意恰当使用字符串和新行符endl,提高输出信息的可读性。
调试技术
1.6 Visual C++的集成开发环境
Visual C++软件包包含了许多独立的组件,如编辑器、编译器、链接器、实用程序生成器、调试器,以及各种各样为开发Windows环境下的C/C++程序而设计的工具。其中最重要的是一个名为Developer Studio的集成开发环境。Developer Studio把所有的Visual C++工具结合在一起,集成为一个由窗口、对话框、菜单、工具栏、快捷键及宏组成的和谐系统,通过该集成环境,程序员可以观察和控制整个开发进程。
图1-2显示了一个典型的Developer Studio主窗口。
从图1-2中可以看出,Developer Studio主窗口可以分为几个部分:窗口顶部是菜单和工具栏,左面的一个子窗口是工作区窗口,工作区的右面是编辑子窗口。最下面是输出子窗口。值得注意的是,上述各种部件,包括子窗口、菜单栏和工具栏的位置不是一成不变的,可以根据个人的喜好重新安排。
1.6.1 菜单和工具栏
Developer Studio中有一个Menu Bar(菜单栏,通常停靠在开发环境窗口的顶部),其中的菜单项有File (文件处理)、Edit(编辑功能)、View(查看)、Insert(插入)、Project(项目管理)、Build(编译)、Tools(工具)、Window(窗口)和Help(帮助)等,分别对应一个下拉子菜单。
除菜单栏外,开发环境中还有几个工具栏,一般均放在开发环境的顶部,菜单栏的下方,如Standard(标准工具栏,用于文件管理、编辑和查看等),Wizard Bar(向导工具栏)和Build MiniBar(建立工具栏,用于编译、连接等)。工具栏上有常用命令的图标。一般来说,工具栏上的命令在菜单中均有对应选项,但工具栏使用更方便,只要用鼠标左键点击工具栏中的相应图标即可调用相应的功能。
开发环境的各种菜单栏和工具栏均为停靠式,可以用鼠标拖动改变它们的位置,也可使用菜单选项Tools/Customize…(表示菜单栏的Tools子菜单中的Customize…选项,下同)中的Toolbars选项卡打开或关闭相应的工具栏,或修改工具栏的内容。
除此之外,Developer Studio的所有部分几乎都可响应鼠标右键单击而弹出一个上下文相关菜单。甚至当Developer Studio没有打开窗口时,在空白区右击鼠标也会弹出一个菜单,其中含有使窗口可见和调整工具栏是否可见的命令。在工具栏上除标题栏外的任何地方右击鼠标,同样可以弹出菜单。在使用集成环境工作时试一试鼠标右键,还会发现许多其他的快捷方式。例如,通过在工具栏或菜单栏上不是按钮或菜单名的地方单击并保持鼠标按键被按下,就可以把它们拖动到屏幕上新的地方。
1.6.2 Developer Studio窗口除了各种对话框外,Developer Studio显示两种类型的窗口,即文档窗口和停靠窗口。文档窗口是一般的带边框子窗口,其中含有源代码文本或图形文档。Window子菜单中列出了在屏幕上以平铺方式还是以层叠方式显示文档窗口的命令。所有其他的Developer Studio窗口,包括工具栏和菜单栏,都是停靠式窗口。开发环境有两个主要的停靠窗口――Workspace(工作区)窗口和Output(输出)窗口。另外还有一个Debugger(调试器)停靠窗口,只在调试过程中显示。
停靠窗口可以固定在Developer Studio用户区的顶端、底端或侧面,或者浮动在屏幕上任何地方。停靠窗口,不论是浮动着的或是固定着的,总是出现在文档窗口的上面。这样,就保证了当焦点从一个窗口移到另一个时,浮动的工具栏一直都是可见的。但这也意味着,文档窗口偶尔会看起来像消失了似的。例如,如果你正在文本编辑器中编辑源代码,此时打开一个占据整个Developer Studio用户区的停靠窗口,源代码文档就会消失,它隐藏在新窗口之下。解决方法是要么关了覆盖的窗口,要么把它拖到不挡眼的地方去。
1.6.3 用Developer Studio编写和调试简单C++程序下面以例1-1为例,说明使用Visual 集成开发环境编写和调试简单C++程序的步骤。
首先打开Developer Studio,选择菜单选项File/New中的Projects(项目)选项卡。从卡中选择Win32 Console Application(32位控制台应用程序)。
为你的应用项目取一名字填写在选项卡右上方的Project Name(项目名称)处,并检查下面的Location(位置)中列出的路径是否正确。注意使下面的单选框选择Create New Workspace(即要建立新工作区),然后按下“OK”键。
这时会出现Application Wizard(应用程序生成向导),提问要生成的项目类型。选择“An Empty Project(空项目)”,按下Finish(结束)键,会弹出一窗口,显示新项目的有关信息。检查无误后按“OK”键。
这时屏上会显示一个空项目,并生成一个工作区文件(后缀为.DSW)。再次选择File菜单的New选项,并选择Files选项卡。从卡中选择C++ Source File(C++源程序)。
为你的程序取一名字(可与项目名相同)填写在选项卡右边的File处,然后按下“OK”键。
这时开发环境右侧的文件编辑窗口中出现了一个空文件,即可输入源程序的内容。
程序输入后,应仔细检查一遍,然后就可以编译了。
选择菜单选项Build/Compile (((.cpp (其中(((为文件名)编译源程序。稍候片刻,会在Output窗口(通常在屏幕下方)中显示编译结果,如出错信息等(编译错误的处理在下一单元介绍)。如果程序正确,编译结果会生成一个目标文件(后缀为.OBJ)。
目标文件还需通过连接才能生成可执行文件。选择菜单选项Build/Build (((.exe连接目标文件。连接的结果同样显示在Output窗口中。连接的结果为可执行文件(后缀为.EXE)。
以上两步也可以并为一步,即直接使用菜单选项Build/Build (((.exe。
生成的可执行文件可以单独运行,也可以在开发环境中直接运行,后一种方法在程序需要反复调试时更加方便。在集成开发环境中直接运行程序的方法是选用菜单选项Build/Excute (((.exe。
1.6.4 菜单选项、快捷键和工具栏
Developer Studio的许多功能都有不只一种调用方法。例如,执行一个编译、连接成功的可执行文件,既可通过选择菜单选项Build/Execute (((.exe完成,也可以直接使用快捷键Ctrl-F5,还可以用鼠标点击Build MiniBar(编译工具栏)上的相应图标来完成。一般来说,越是常用的功能,可以调用的方法越多、越方便。
初学者应特别注意的是,通常一个项目只对应一份程序。因此,在结束一个程序之后,应退出当前项目(使用Developer Studio的菜单选项File/Close Workspace),然后再为新程序重建一个新的项目。
程序设计举例
[例1-2]计算太阳和地球之间的万有引力。
算法分析,由普通物理知,两个质量分别为m1和m2的物体之间的万有引力F与两个物体质量的乘积成正比,与两个物体质心之间的距离R的平方成反比:
式中的G为引力恒量,其具体数值与式中各量的量纲有关。如果取质量的单位为克,距离的单位为厘米,力的单位为达因,则
G ≈6.666667×10-8厘米 /克·秒因此,只要将太阳的质量1.987×1033克和地球的质量5.975×1027克以及两者之间的距离1.495×1013厘米代入上式,即可算出太阳和地球之间的万有引力。
程 序
// Example 1-2:计算太阳和地球之间的万有引力
#include <iostream.h>
double grav(double m1,double m2,double distance)
{
double g,G = 6.66667E-8;
g = G*m1*m2/(distance*distance);
return g;
}
void main()
{
double g,Msun = 1.987E33,Mearth = 5.975E27;
g = grav(Msun,Mearth,1.495E13);
cout << "The gravitation between sun and earth is "<<g<<" Dyne." << endl;
}
输 出,The gravitation between sun and earth is 3.5413E+27 Dyne.
分 析,在设计该程序时,我们将计算任意两个质点之间的引力公式单独编写为一个函数grav(),然后在主函数中调用该函数具体计算太阳和地球之间的万有引力。这样做有两个好处:
(1)首先,简化了主函数的编写。在编写主函数时,可以认为已经有了一个用于计算万有引力的现成函数grav(),我们只需要按要求填写好参数后调用该函数就可以得到计算结果,和调用标准的库函数方法完全相同。这样即使grav()由其他程序员编写,我们在不必了解其内部结构的情况下也可以方便地调用之;
(2)如果以后还要计算其他物体之间的引力,例如地球和月球之间的万有引力,就不必再次编写相应的程序段了,只需在调用grav()函数时换上相应的参数即可。
按这种方法设计程序就称为模块化程序设计,是结构化程序设计方法(我们将在第二单元中详细介绍结构化程序设计方法)的一部分。
[例1-3] 加法计算器程序。
程 序:
// Example 1-3:加法计算器程序
#include <iostream.h>
void main()
{
double a,b,c;
cout<<"Please input two numbers,";
cin>>a>>b;
c = a+b;
cout << a << " + " << b << " = " << c<< endl;
}
输 入,Please input two numbers,12.0 34.0
输 出,12 + 34 = 46
分 析,本例使用了双精度浮点类型的变量进行运算,所以可以计算小数加法。程序在接收输入数据之前首先显示一行提示信息,告诉用户应该如何输入数据,并在输出结果时同时输出了计算公式。这些作法都是为了方便程序的用户,是编写应用程序的基本要求。
[例1-4] 显示生日卡。该程序首先要求输入收信人和发信人的姓名,然后在屏幕上显示出完整的生日卡来。
程 序:
// Example 1-4:显示生日卡
#include <iostream.h>
void main()
{
char name1[41],name2[41];
cout<< endl << "Please input your friend's name,";
cin >> name1;
cout<< endl << "Please input your name,";
cin >> name2;
cout<< endl << "====================================" << endl;
cout<< "My dearest " << name1 << "," << endl;
cout<< " Happy birthday to you!" << endl;
cout<< " yours," << endl;
cout<< " " << name2 << endl;
cout<< "====================================" << endl;
}
输 入,Please input your friend's name,Zhang
Please input your name,Li
输 出,====================================
My dearest Zhang,
Happy birthday to you!
yours,
Li
====================================
分 析,该程序可以通过输入收信人和发信人的姓名来构造相应的生日卡。注意程序中使用了字符类型的数组存放表示收信人和发信人名字的字符串,数组中的每个元素用于存放一个字母。
单元上机练习题目
1,在计算机上通过运行本单元的各例题,熟悉Developer Studio的使用方法。
2,乘法计算器程序:请同学们根据例1-3自行改编。
3,修改例1-4的生日卡程序,使其能够输入和显示日期。
4,使用梯形法计算定积分,其中a = 0,b = 1,被积函数为sin(x),取积分区间等分数为 1000。
计算原理和方法,将积分区间等分为n份,其中第i个小区间上的定积分
可以使用如图1-3所示的梯形的面积来近似。
因此整个积分区间上的定积分可表示为
程 序:
// Example 1-5:用梯形法计算定积分
#include <iostream.h>
#include <math.h>
// 定义被积函数
double f(double x)
{
return sin(x);
}
// 主函数,用梯形法计算定积分
void main()
{
double a,b; // 双精度类型变量,积分的下限和上限
double h; // 双精度类型变量,积分步长
double sum; // 双精度类型变量,工作变量,最后为积分值
int n; // 整型变量,积分区间等分数
int i; // 整型变量,循环工作变量
// 根据题意确定积分的下限、上限和积分区间等分数
a = 0.0;
b = 1.0;
n = 1000;
h = (b-a)/n; // 计算小区间长度
// 为工作变量赋初值,先计算不易循环运算的部分
sum = (f(a)+f(b))/2;
// 循环计算公式中的Σ和式
for(i=1;i<n;i=i+1)
sum = sum+f(a+i*h);
// 完成计算,变量 sum 中存放积分结果
sum = sum*h;
// 输出计算结果
cout<<"The result is " << sum << endl;
}
5.改编上题的程序,使之可以用来计算定积分:
积分区域等分数可取为200,并将以上两题的计算结果和手算结果相比较。