第3章数据类型
本章视频教学录像:1小时03分钟
在第一个C++例子中,我们已经学会了如何将我们想的内容输出到C++的输出窗口。我们可能看到过一些程序可以进行相互操作并具有运算的功能。下面我们要在程序中添加这些相互操作和运算的功能。另外,通过第2章我们已经知道C++函数中语句包含了关于数据变量的定义。我们需要数据,并要操作这些数据。计算机处理的对象是数据,为了描述不同的对象而会用到不同的数据。为了提高数据的运算效率和数据的存储能力,本章将引入数据类型的概念。根据数据是否可以变化,数据分为变量和常量。
本章要点(已掌握的在方框中打钩)
□ 整型
□ 字符型
□ 浮点型
□ 常量
□ 变量
□ 变量和常量的声明
3.1 数据类型
本节视频教学录像: 9分钟
数据是进行程序设计的基础,在C++中定义了很多的数据类型,本节将介绍常用的数据类型及其基本用法。常用的数据类型包括整型、浮点型、字符型数据。
3.1.1 整型
我们已经知道了计算机是如何识数的,那么,在本小节我们将介绍计算机中最常用同时也是最简单的整型数据。“整型”就是不含小数部分的数值,包括正整数、负整数和0,用int表示。
int类型数据特点如下。
首先来看整型常量。整型常量有3种表示方法。
⑴ 十进制形式,如26、-29、0和12000。
⑵ 八进制形式,以数字0开头,由0~7之间的数字组成的数据,如056和-0234。
⑶ 十六进制形式,以0x或0X开头,由数字0~9和字母A、B、C、D、E、F组成的数据。如0X5A、0x39和-0x5b。注意,这里字母不区分大小写。
整数类型的变量的值都为整数。计算机中的整数与数学意义上的整数有很大的区别。数学上的整数可从负无穷大到正无穷大,而计算机中的整数类型有多种,并且值都是有范围的。整型按照表示长度有8、16、32位之分,具体如表所示。
有了类型和符号形式,就可以得到整型数值的表示范围,公式如下。
① 有符号形式:L= -2n-1,U=2n-1-1
② 无符号形式:L=0,U=2n-1
其中,L表示范围的下限,即整型数据可以表示的最小数值;U表示范围的上限,即整型数据可以表示的最大数值;n表示类型,即位长。例如,short int,即默认的signed short int,其表示的数据范围为-215~215-1,即-32768~32767。
3.1.2 浮点型
浮点数也称为实型数就是我们平时所说的小数,有两种精度的浮点型数据,一种是单精度的float类型,一种是双精度的double类型,它们的区别是精度不一样,小数点后面的精确位数double比float多。实型数有两种表示形式:小数表示法和指数表示法。
⑴ 小数表示法
使用这种表示形式时,实型常量分为整数部分和小数部分。其中的一部分可在实际使用时省略,如10.2、.2、2.等,但整数和小数部分不能同时省略。
⑵ 指数表示法
指数表示法也称科学记数法,指数部分以E或e开始,而且必须是整数。如果浮点数采用指数表示法,则E或e的两边都至少要有一位数。如以下数是合法的:1.2e20、-3.4e-2。
浮点数在计算机中同样也必须要用二进制表示,分两部分:整数部分和小数部分。整数部分的转换不用说了,小数部分采用和整数部分相反的方法进行,即“乘2取整法”,这里不再讨论。
3.1.3 字符型
字符型的数据是不同于整型和浮点型的一类数据类型,不是由数字组成的。可以说除了上面介绍的两种类型数据,其他都是字符类型的数据,其实整型和浮点型也可以看成是字符类型。另外,键盘上可以打出来的内容都可以看成字符,包括空格、回车等。计算机存储字符类型的数据是通过这个字符所对应ASCII来存储。
这里要特别说明的是,ASCII和一种特殊字符转义字符。ASCII有128个字符,其中ASCII值从0~31和127为不可见字符,也就是控制字符。由此不难看出,在不太苛刻的条件下,上面两种方法分别表示了可见字符和不可见字符。仅仅如此还不足以体现字符型的个性所在。在C++中,允许用一种特殊形式的字符,即以“\”打头的格式字符,这就是转义字符。下表列出了C++中转义字符的所有形式。
3.2 常量与变量
本节视频教学录像: 18分钟
C++程序除了规定的语句之外,还需要操作一些数据,正如在数学中有已知数和未知数一样,C++中也有常量和变量之分。那么什么是常量?怎么使用常量?变量是什么?如何声明变量?如何使用变量?这些就是接下来要学习的内容。
常量,顾名思义就是值不会改变的量。常量类型一般有3种形式,分别是用于输出的常量、用宏定义字符常量,还有一种就是有const修饰的值不可以变化的变量(习惯上叫常量)。
3.2.1 输出常量
用于输出的常量有数值常量、字符常量、字符串常量。
数值常量即是通常所说的常数,可以是整形、实数型,如234、12、6、0.9、89.67等,也可以是负数,如-5、-89、-98.76等,当然也可以是0。如果是负数,一定要带上符号“-”;如果是正数,可以不带符号“+”,当然也可以带上。
C++的字符常量是用英文单引号括起来的一个字符,如'a'、'F'、'3'、'!'、'%'等。字符常量中的单引号仅起分界符的作用,存储和使用时都不包括单引号。在输出时要用''将它引起来。
字符常量以单引号作为分界符,单引号是英文标点符号,不能是中文标点符号。上面介绍数据类型、字符类型时的转义字符也是英文标点符号。
注意
数值常量3和字符常量'3'代表不同的含义,3表示数的大小,而'3'仅仅表示符号。后者就像电话号码中的数字,没有人去想到它的大小。在计算机内存中,3和'3'的保存方式也是不一样的。
C++的字符串常量是用双引号括起来的字符序列。
任何字母、数字、符号和转义字符都可以组成字符串,下面举例说明。
⑴ ""是空串。
⑵ " "是空格串,而不是空串。
⑶ "b"是由一个字符b构成的字符串。
⑷ "Happy Birthday"是由多个字符序列构成的字符串。
⑸ "abc\t\n"是由多个包括转义字符在内的字符构成的字符串。
注意
"A"和'A'不一样,前者是只有一个字符的字符串常量,而后者是字符常量。
提 示
"A"和'A'究竟有什么不同呢?
C++规定:在每一个字符串的结尾加一个“字符串结束标记”,以便系统能据此判断字符串是否 结 束。字 符 串 结 束 标 记 就 是 '\o'。所 以 在 计 算 机 内 存 中 "A" 其 实 占 了 两 个 字 符 存 储 位 置,一 个是字符'A',一个是字符'\o'。
符串常量与字符常量的区别如下。
⑴ 书写格式不同:字符常量用' '(单引号),而字符串常量用" "(双引号)。
⑵ 表现形式不同:字符常量是单个字符,字符串常量是一个或多个字符序列。
⑶ 存储方式不同:字符常量占用一个字节,字符串常量占用一个以上的字节(比字符串的长度多一个)。
由双引号" "括起来的字符序列中的字符个数称为字符串的长度,字符串结束符'\0' 并不计算在字符串的长度内(在定义字符串常量时,这个结束符不需要给出,C++语言会自动加上,但是存储时,空字符将会额外地占用一个字节空间)。比较容易出错的是,当字符串中出现转义字符时字符串长度的确定。转义字符从形式上看是多个字符,而实际中它只代表一个字符,因此在计算字符串的长度时容易将它看成多个字符计入长度中。
提示
字符串“\\\"sam\"\\\n”的长度是多少?
它的长度为8。
第1个字符是转义形式字符'\\',第2个字符是转义形式字符'\"',3到5为字符s、a、m,接下来是第6个字符'\"',第7个是转义形式字符'\\',最后第8个是'\n'。最终的输出形式为\"sam\",然后换行。
【范例3-1】 认识常量。
⑴ 在Visual C++ 6.0中,新建名为“helloConstant”的【C++ Source File】源程序。
⑵ 在编辑窗口中输入以下代码(代码3-1.txt)。
01 #incIude<iostream> 02 using namespace std; 03 int main() 04 { 05 cout<<”认识C++中常量”<<endI; 06 cout<<34<<endI; //在命令行中输出“34”并回车 07 cout<<1.8<<endI; //在命令行中输出“1.8”并回车 08 cout<<-78<<endI; //输出-78 09 cout<<'b'<<'a'<<'g'<<'\t'<<'\''<<'!'<<'\''<<'\n'; //输出3个字符b、a、g,然后输出一个制表符、一个单引号'、一个感叹号!、一个单引号',最后回 车换行 10 cout<<"This is a book!"<<endI; //在命令行中输出“This is a book!”并回车 11 return 0; ///函数返回0 12 }
【代码详解】
endl的作用是输入一个回车并刷新缓冲区,其中输入回车的功能也可以使用“\n”来代替。
由于入口函数使用了int main(),所以在程序结束时需要返回一个整数,“return 0;”的作用就体现在此。如果使用void main(),就不需要加return语句。另一方面,“return 0;”和“return EXIT_SUCCESS;”的功能一样,标志程序无错误退出。
【运行结果】
编译、连接、运行程序,即可在命令行中输出各个常量,如图所示。
【范例分析】
在本例中有字符串常量、整型常量、字符常量,其实是很简单的一个实例。
技巧
C++语言中的整型常量可不能像数学中的整数一样,个、十、百、千、万、十万、百万、千万、亿,爱写多少位就写多少位,C++中的整数大到一定程度就再也不能大了。
在C++中,数值常量如果大到一定的程度,程序就会出现错误,无法正常运行,这是为什么呢?原来,C++程序中的量,包括我们现在学的常量,也包括后面要学到的变量,在计算机中都要放在一个空间里,这个空间就是常说的内存。你可以把它们想成是一个个规格定好了的盒子,这些盒子的大小是有限的,不能放无穷大的数据。
注意
有些字符因为已经被用来做界限符,比如单引号、双引号和反斜杠,所以如果要输出这些字符,就必须使用转义字符。
3.2.2 宏定义的符号常量
当某个常量引用起来比较复杂而又经常要被用到时,可以将该常量定义为符号常量,也就是分配一个符号给这个常量,在以后的引用中,这个符号就代表了实际的常量。这种用一个指定的名字代表一个常量称为符号常量,即带名字的常量。在C++的语言世界里我们常用宏来使符号常量得以简化。使用宏可提高程序的通用性和易读性,减少不一致性,减少输入错误并便于修改。
在C++语言中,允许将程序中的常量定义为一个标识符,这个标识符称为符号常量。其中的标识符就是所谓的符号常量,也称为“宏名”。符号常量必须在使用前先定义,定义的格式为:
#define符号常量 常量
一般情况下,符号常量定义命令要放在主函数main()之前,如:
#define PI 3.14159
该命令的意思是用符号PI代替3.14159。在编译之前,系统会自动把所有的PI替换成3.14159,也就是说编译运行时系统中只有3.14159而没有符号。
【范例3-2】 符号常量。
⑴ 在Visual C++ 6.0中,新建名为“signconstant”的【C++ Source File】源文件。
⑵ 在编辑窗口中输入以下代码(代码3-2.txt)。
01 #define PI 3.14159 //定义符号常量PI 02 #incIude<iostream> 03 using namespace std; 04 int main() 05 {
06 int r; //定义一个变量用来存放圆的半径 07 cout<<“请输入圆的半径:”; //提示用户输入圆的半径 08 cin>>r; //输入圆的半径 09 cout<<”\n圆的周长为:”; 10 cout<<2*PI*r<<endI; //计算圆的周长并输出 11 cout<<”\n圆的面积为:”; 12 cout<<PI*r*r<<endI; //计算圆的面积并输出 13 return 0; 14 }
【运行结果】
编译、连接、运行程序,根据提示输入圆的半径6,按【Enter】键,程序就会计算圆的周长和面积并输出,如图所示。
【范例分析】
在这个例子中我们用到了运算问题,有关这方面的内容将在后面介绍。由于我们在程序前面定义了符号常量PI的值为3.14159,所以经过系统预处理,程序在编译之前已经变成如下的形式。
01 #incIude<iostream> 02 using namespace std; 03 int main() 04 { 05 int r; 06 cout<<“请输入圆的半径:”; 07 cin>>r; //将输入的半径值赋值给r 08 cout<<”\n圆的周长为:”; 09 cout<<2*3.14159*r<<endI; //*在C语言中为乘法操作此为求取周长操作 10 cout<<”\n圆的面积为:”; 11 cout<<3.14159*r*r<<endI; //*在C语言中为乘法操作此为求取面积操作 12 return 0; 13 }
步骤⑵中代码第1行的#define就是预处理命令。C++在编译之前首先要对这些命令进行一番处理,在这里就是用真正的常量值取代符号。
有的人可能会问,那既然在编译时都已经处理成常量,为什么还要定义符号常量。原因有如下两个。
⑴ 易于输入,易于理解。在程序中输入PI,可以清楚地与数学公式对应,且每次输入时相应的字符数少一些。
⑵ 便于修改。此处如果想提高计算精度,如把PI的值改为3.1415926,只需修改预处理中的常量值,那么程序中不管用到多少次,都会自动跟着修改。
提示
⑴ 符号常量不同于变量,它的值在其作用域内不能改变,也不能被赋值。⑵ 习惯上,符号常量名用大写英文标识符,而变量名用小写英文标识符,以示区别。⑶ 定义符号常量的目的是提高程序的可读性,便于程序的调试和修改。因此在定义符号常量名时,应尽量使其表达它所代表的常量的含义。⑷ 对程序中用双引号括起来的字符串,即使与符号一样,预处理时也不做替换。
【拓展训练】
动手试一试下列程序的运行结果跟你想象的结果一样吗?分析一下原因。
01 #define X 3+2 02 #incIude<iostream> 03 using namespace std; 04 int main() 05 { 06 cout<<X*X<<endI; 07 cout<<X+X<<endI; 08 return 0; 09 }
在这里,符号常量代表表达式3+2。经过编译预处理后,程序变成了下面的样子。
01 #incIude<iostream> 02 using namespace std; 03 int main() 04 { 05 cout<<3+2*3+2<<endI; //用3+2代替X 06 cout<<3+2+3+2<<endI; //用3+2代替X 07 return 0; 08 }
所以输出应该分别为11和10。因为3+2×3+2中先计算2×3,而不是先计算3+2。
由此可见,预处理时只是简单地取代符号,不是先进行计算。
提示
如果想计算(3+2)×(3+2)的结果,该怎么定义符号常量?
只需要把“#define X 3+2”改为“#define X(3+2)”即可。那样编译预处理后程序就变成下面的样子。
01 #incIude<iostream> 02 using namespace std; 03 int main() 04 { 05 cout<<(3+2)*(3+2)<<endI; //用(3+2)代替X 06 cout<<(3+2)+(3+2)<<endI; //用(3+2)代替X 07 return 0; 08 }
3.2.3 const常变量
我们查阅字典后会发现,单词const在中文里的意思就是常量。在定义变量时,如果加上关键字const,则变量的值在程序运行期间不能改变,这种变量称为常变量(constant variable)。也就是说用const定义的是常量而不是变量,它的值只能读而不能写(修改),例如下面的语句。
const int a=3; //用const来声明这种变量的值不能改变,指定其值始终为3 const常量定义格式为:const类型名 常量名=常量值
例如上面定义了一个常量a,它的值为3。
在定义常变量时必须同时对它初始化(即指定其值),此后它的值不能再改变。而常变量不能出现在赋值号的左边。上面一行语句不能写成如下形式。
const int a; a=3; //常变量不能被赋值
可以用表达式对常变量初始化,如下所示。
const int b=3+6,c=3*2; //b的值被定义为9,c的值被定义为6
有些读者自然会提这样的问题:变量的值应该是可以变化的,为什么值是固定的量也称变量呢?的确,从字面上看,常量的名称本身包含着矛盾。其实,从计算机实现的角度看,变量的特征是存在一个以变量名命名的存储单元,在一般情况下,存储单元中的内容是可以变化的。对常变量来说,无非是在此基础上加上一个限定:在存储单元中的值不允许变化。因此常变量又称只读变量。
常变量这一概念是从应用需要的角度提出的,例如有时要求某些变量的值不允许改变(如函数的参数),这时就用const加以限定。除了变量以外,以后还要介绍指针、常对象等。
表示在编译时分配2个字节大小的内存空间,以后变量i的值就从这2个字节单元中取得。
变量有以下4个属性。
⑴ 变量名称:表示变量的一个标识符,符合标识符的命名规则。
⑵ 数据类型:一个变量必须属于C++中的某种数据类型,如整型int、字符型char等。
⑶ 变量地址:是系统分配给变量的内存单元编号。C++语言可以用&(地址运算符)加变量名称求得一个变量的地址,例如&i。这方面的知识在学完指针后会更清晰。
⑷ 变量的值:定义一个变量的目的就是为了使用它的内容(值),没有值的变量是没有意义的。大部分数据类型的变量值可以用名称直接表示,例如i =3,就代表变量i的值为3。
变量名称和数据类型由用户定义说明,变量地址由系统决定。变量的值来自程序中的赋值。给变量赋值的方法有两种:初始化和赋值语句。赋值语句在第2章中已经有所接触,下面重点介绍变量的初始化。
在C++语言中允许在变量声明的同时对变量赋值,称为变量的初始化,也叫变量赋初值。在程序设计中常常需要对变量赋初值。
语法格式:
类型说明符 变量名=初始数据;
其中“=”是赋值运算符,表示将初始数据存入变量名所代表的内存单元。
例如int k=14,表示定义一个整型变量k的同时将k所代表的存储单元存入数据14。
如果相同类型的几个变量需要同时初始化,可以在一个声明语句中进行。
语法格式为:
类型说明符 变量名1=初始数据1,…,变量名n=初始数据n;
例如int a=8,b=8,c=8,表示a、b、c的初始值均为8,但并不表示整个程序中3个变量值一直不变或一直相等。
也可以对被定义的变量中的一部分赋初值。
例如:
int a=3,n; /*定义a、n为整型变量,只有a被初始化,且a的初始值为3*/ float b,c=10.1; /*定义b、c为实型,且c的初值为10.1*/
该语句相当于:
int a,n; float b,c; a=3;c=10.1;
但是二者之间有一定的差别,后一种实际上是程序执行过程中由赋值语句对变量赋值。
3.3 变量
本节视频教学录像:19分钟
我们已经见过C++常量,在个别例子中也不可避免地看到了变量的影子,那什么是变量?变量怎么定义?变量如何初始化?
3.3.1 什么是变量
在数学学习中,对于一个变化的量,可以用一个字符代替,并称之为变量。同样在C++,为了存放一个值可以发生改变的量,我们也有变量的概念。
C++程序中出现的每个变量都是由用户在程序设计时定义的。C++规定,所有的变量必须先定义后使用,变量名必须按照C++语言规定的标识符命名原则命名。在同一个函数中变量名不要重复,即使它们是不同的数据类型。
在学习变量时要了解的两个概念是标识符、关键字。
标识符,就像我们给孩子起的名字一样,但是在C++和其他的编程语言中,标识符的定义有一定的规则。另外,关键字就是这些词语已经被系统征用了,用户不可以使用。
标识符的特点如下。
⑴ 由于C++语言严格区分大小写字母,因此Sum和sum被认为是不同的变量名。为了避免混淆,应该使用不同的变量名,而不是通过大小写来区分变量。
⑵ 对变量名的长度(标识符的长度)没有统一的规定,随系统的不同而有不同的规定。一般来说,C++编译器肯定能识别前31个字符,所以标识符的长度最好不要超过31个字符,这样可以保证程序具有良好的可移植性,并能够避免发生某些令人费解的程序设计错误。许多系统只确认31个有效字符,所以在取名时,名称的长度应尽量在31位有效字符之内。
⑶ 在选择变量名和其他标识符时应注意做到“见名知义”、“常用取简”、“专用取繁”,例如count、name、year、month、student_number、display、screen_format等,使人一目了然,以增强程序的可读性。即用有含义的英文单词或英文单词缩写做标识符。
关键字,像int、double、har、include、for……在我们使用时,其字的颜色会变成蓝色。C++中的关键字有63个。
3.3.2 变量的定义
变量名实际上是和计算机内存中的存储单元相对应的,每一个变量都有一个名字、一种类型、一个具体的值。
定义一个变量,需要做以下两件事情。
⑴ 定义变量的名称。按照标识符的规则,根据具体问题的需要任意设置。
⑵ 给出变量的数据类型。根据实际需要设定,设定的数据类型必须是系统所允许的数据类型中的一种。
在C++语言中,定义一个变量的完整格式是:
存储类别名 数据类型名 变量名1=表达式1,…,变量名n=表达式n;
其中存储类别名有static、extern、auto等,我们在后面的学习中会学到。
C++语言允许将值放在变量中,每个变量都由一个变量名来标识。每个变量都有一个变量类型,变量类型告诉C++该变量随后的用法以及保存的类型。定义一个变量的过程实际上就是向内存申请一个符合该数据类型的存储单元空间的过程,因此可以认为变量实质上就是内存某一单元的标识符号。对这个符号的引用,就代表了对相应内存单元的存取操作。定义变量时给出的数据类型一方面是编译系统确定分配存储单元大小的依据,同时也规定了变量的取值范围以及可以进行的运算和处理。在C++语言中,定义变量是通过声明语句实现的。
声明语句的功能是定义变量的名称和数据类型,为C++编译系统给该变量分配存储空间提供依据。
语句格式是:
类型说明符 变量表:
其中类型说明符可以是int、long、short、unsigned、float,double、char,也可以是构造类型。构造类型在后面会学到。变量表是想要声明的变量的名称列表,C++语言允许在一个类型说明符之后同时说明多个具有相同类型的变量,此时各个变量名之间要用逗号“,”分隔开,例如:
int i,a23,x_123;
其中:int为类型说明;i、a23、x_123为3个变量名,之间用逗号分隔。
这个变量定义说明了有几个相同类型的变量(3个)、变量叫什么名(i、a23、x_123)以及用来做什么(参与整型数据的处理)。任意一个变量都必须具有确定的数据类型,不管变量值怎样变化,都必须符合该类数据类型的规定(形式和规则两个方面)。同样,我们也可以一下定义4个、5个同样类型的数据。但是有时,在一个类型后面跟太多变量,看着麻烦,我们可以分开定义。
3.3.3 变量的赋值
在数学中有一个变量后,函数计算是给变量一个值,然后,让变量参加运算就可以得到结果。在C++中也需要对变量进行赋值后才可以它参加运算。怎样给变量赋值呢?
变量赋值有以下两种形式。
直接复制,又叫初始化,如下所示。
int a=10;float b=2.0; char c=’A’;
另外一种是先定义变量,在要用该变量时再进行赋值运算。
int a; int b; b=10; //这里是通过一个数值赋值 a=b; //这里是通过一个变量给另个变量赋值
对于变量赋值,左边是要赋值的变量,右边是要赋值的数值或字符。注意左边只可以是单一一个变量名,右边则可以是数值、一个有确定数值或字符的变量、一个运算式、一个函数调用(后面将讲到)后的返回值。但是还要注意一点的是,函数赋值必须是相同类型的数值之间的赋值,不可以是int赋值给char。
下面我们就通过几个例子,对前面讲到的数据类型和变量定义赋值做一个系统了解。
例如,下面的程序段中包含两个错误。
main() { int x,y; x=10;y=20;z=30; /*z未定义即被赋值*/ sum=x+y*z; /*sum先使用,后定义*/ int sum; }
下面的程序段中包含一个错误。
main() { int a; float a,x,y; /*a被定义了两次*/ a=x+y; }
提示
在C++中,标识符用来定义变量名、函数名、类型名、类名、对象名、数组名、文件名等,只能由字母、数字和下划线等组成。且第1个字符必须是字母或下划线,切不可用系统的关键字。例如sum、a、i、num、x1、area、_total等都是合法的变量名,而int、float、2A、a!、x 1、100等都不是合法的变量名。标识符应注意做到“见名知义”、“常用取简”、“专用取繁”,习惯上符号常量、宏名等用大写字母表示,变量、函数名等用小写字母表示,系统变量则以下划线开头。
【范例3-3】 定义变量并赋值,并在命令行中输出这些变量。
⑴ 在Visual C++ 6.0中,新建名为“helloVar”的【C++ Source File】源文件。
⑵ 在编辑窗口中输入以下代码(代码3-3.txt)。
01 #incIude<iostream> 02 using namespace std; 03 int main() 04 { 05 int x=1,y; //定义两个整型变量x和y,其中x赋初值1 06 doubIe r=1.0; //定义一个双精度型变量r并赋初值1.0 07 char a='a'; //定义字符型变量a并赋初值a 08 y=x+1; //x加1赋值给y 09 cout<<”x=”<<x<<'\t'<<”y=”<<y<<'\t'<<”r=”<<r<<'\t'<<”a=”<<a<<endI; //输 出结果 10 return 0; 11 }
【运行结果】
编译、连接、运行程序,即可在命令行中输出如图所示的结果。
【范例分析】
变量必须先定义后使用。此处一共定义了4个变量,分别为两个整型变量x和y,一个双精度型变量r,一个字符型变量a。变量相当于一个存放东西的盒子,那么东西什么时候放进盒子里呢?有两种方法,其一是在定义变量时直接放进去,即定义变量并同时进行初始化,如本例的变量x、r、a;其二是在程序中用赋值语句给变量赋值,如本例的变量y。
【范例3-4】 输入整型数据运算后输出。
⑴ 在Visual C++ 6.0中新建名为“PutoutInt”的【C++ Source File】源文件。
⑵ 在编辑窗口中输入以下代码(代码3-4.txt)。
01 #incIude<iostream> 02 using namespace std; 03 void main() 04 { 05 short int a; //声明一个短整型数据 06 cout<<"请输入一个短整型:"; //显示提示内容 07 cin>>a; //输入数据赋值给a 08 cout<<"你输入的是:"<<a<<endI; //输出数据a到屏幕 09 }
【代码详解】
endl的作用是输入一个回车并刷新缓冲区,其中输入回车的功能也可以使用“\n”来代替。
因为入口函数使用void main(),就不需要加return语句。如果入口函数使用了int main(),则在程序结束时需要返回一个整数,“return 0;”的作用就体现在此。另一方面,“return 0;”和“return EXIT_SUCCESS;”的功能一样,标志程序无错误退出。这些在后面的代码中将会得到体现。
【运行结果】
编译、连接、运行程序,按照提示分别输入-32800、-32768、0、32767和32800,并按【Enter】键,结果如图所示。
【范例分析】
在这个实例中,先定义了短整型变量a,接着输入a的值,然后输出其值,程序十分简单,结果却并不简单。这里按照短整型的取值范围进行测试,取5个值,具有如下分布。
5个点代表了所有的情形。可以看出,出现超限即溢出的情形是不好解释的,其实,不同的编译器有不同的理解方式,大可不必去追究太多,但是必须要知道这是一个异常。
【范例3-5】 字符型数据的输出。
⑴ 在Visual C++ 6.0中,新建名为“PutoutChar”的【C++ Source File】源文件。
⑵ 在编辑窗口中输入以下代码(代码3-5.txt)。
01 #incIude<iostream> 02 using namespace std; 03 void main() 04 { 05 int a=7; //定义整型变量a并赋初值7 06 char b=7; 07 char c=’7’; //定义字符型变量b并赋初值7 08 cout<<a<<b<<endI; 09 cout<<c<<endI; //输出a和b 10 }
【运行结果】
编译、连接、运行程序,即可在控制台中输出7,如图所示,并伴随“嘀”的一声。
【范例分析】
a是整型数据,就显示7;b是字符型数据,将按控制字符的要求做一次响铃;而c也是字符型数据,因为带有引号标示,所以可以输出c的字符7。三者的值都是7,但是其显示的结果却是天差地别,只因为类型不同。
【拓展训练】
如果将最后一句变成“cout << a+ b;”将会如何?大家试一试吧!看到结果后能不能自己分析一下?科学就要大胆猜想,仔细求证。
3.3.4 变量的生存周期
对象的生存周期限制在其出现的“完整”的表达式中,“完整”的表达式结束了,对象也就销毁。生存周期是指一个实体定义以后存活的时间的度量。我们目前已学到的知识还很有限,等到学习了函数、类以后,将进行更深入的讨论。但是我们务必要明白,一个变量并非在任何地方都是可见的,也不是在任何时候都是存在的。
3.4 数据类型转换
本节视频教学录像: 12分钟
提示
能将一个浮点常量赋值给整型吗?
C++中有明确的规定,不同类型的数据之间不能参加任何运算。这是个铁的纪律,谁都要遵守。但是明明可以将浮点值赋值给整型的啊!
通过前面的学习,我们已经了解到C++语言中对数据的“门派观念”是很严格的。这一点虽然对代码的执行效率和安全性以及规范性等都起到了极为重要的作用,但是,森严的等级和严格的门户之见也必将引起很多纠纷和不便。
那么两者如何协调?解决纷争的关键问题是什么呢?
类型转换应运而生了。所谓类型转换,就是将一种数据类型转换为另外一种数据类型。一般来说有如下的转换次序。
字符型→短整型→整型→单精度浮点型→双精度浮点型
这一顺序按照数据类型由低到高排列,所有的这些转换都是由系统自动进行的,使用时只需了解结果的类型即可。这些转换可以说是自动的,转换的次序是系统规定好了的,那么可以打破这个次序吗?当然可以!C语言也提供了以显式的形式强制转换类型的机制,我们可以强制转换,其语法如下。
(要转换的新的数据类型)被转换的表达式
当较低类型的数据要转换为较高类型时,一般只是形式上有所改变,而不影响数据的实质内容;而较高类型的数据转换为较低类型时则可能会使有些数据丢失。
上面介绍的是旧风格的,或者说是C语言风格的转换方式。这种方式的缺点有以下两点。一来它们过于粗鲁,能允许你在任何类型之间进行转换;二来C风格的类型转换在程序语句中难以识别。C++通过引进4个新的类型转换操作符克服了C风格类型转换的缺点,这4个操作符是static_cast、const_cast、dynamic_cast和reinterpret_cast。
static_cast在功能上基本上与C风格的类型转换一样强大,含义也一样,它也有功能上的限制。const_cast用于类型转换表达式的const或volatileness属性。dynamic_cast被用于安全地沿着类的继承关系向下进行类型转换。reinterpret_cast是专门用于底层的强制转型。
这里以static_cast为例说明其用法。
static_cast <要转换的新的数据类型>被转换的表达式
【范例3-6】 C++中的类型转换。
⑴ 在Visual C++ 6.0中,新建名为“simplesort”的【C++ Source File】源文件。
⑵ 在编辑窗口中输入以下代码(代码3-6.txt)。
01 #incIude<iostream> 02 using namespace std; 03 void main() 04 { 05 int firstNumber,secondNumber; //定义两个整型变量 06 firstNumber=9; //firstNumber赋值为9 07 secondNumber=43; //secondNumber赋值为43 08 doubIe resuIt=static_cast<doubIe>(firstNumber)/secondNumber; 09 //定义双精度变量resuIt并赋初值, 10 //static_cast<doubIe>将firstNumber强制转换为doubIe,也可以用语句 11 //doubIe resuIt=(doubIe)firstNumber/secondNumber;来实现这一功能 12 cout<<resuIt<<endI; //输出结果 13 return; 14 }
【代码详解】
从功能上看,这是一个简单的程序,实现了两个数的相除运算。
第5~7行定义了两个整型变量并赋了值。
第8行中利用static_cast实现了强制类型的转换。为了和旧的风格相比较,在第11行中写出了C语言风格的转换语句。
第12行是一个输出语句,用到了标准输出流。
第13行是返回语句,将程序控制权回交给系统。
【运行结果】
编译、连接、运行程序,即可在控制台中输出如图所示的结果。
将代码中的第8行的强制转换去掉,重新编译、连接、运行程序,结果如图所示。这是因为进行了整数相除。
【范例分析】
在本范例中,用到了两次类型转换。第1次在【代码详解】中已经说明,也是重点部分。第2次发生在什么时候呢?同样也是在第8行。实际上,当把firstNumber强制转换为double后,secondNumber在和其进行相除的运算中也被强制转换为double,然后才能得到浮点除法的结果。这一次转换是系统自动进行的,这因为先前我们说的那一条铁的原则——不同类型的数据不能进行运算。其转换原则由转换次序决定。
注意
本例中的firstNumber和secondNumber自始至终一直没有改变。类型转换不会影响变量本身的值。
【范例3-7】 C++中的类型转换。
⑴ 在Visual C++ 6.0中,新建名为“numCount”的【C++ Source File】源文件。
⑵ 在编辑窗口中输入以下代码(代码3-7.txt)。
01 #incIude<iostream> 02 using namespace std; 03 int main() 04 { 05 int num,count1=0,count2=0; //定义3个整型变量,其中num用来接收输入的数值,count1用来统计正数个数,count2用来统计负 数个数 06 doubIe sum=0.0,ave=0.0; //定义存放总和与平均值的两个变量sum和ave 07 cout<<"请输入若干个正整数,以0结束输入:\n"; 08 cin>>num; 09 whiIe(num!=0) //当输入的数字不为0时进行大括号内的操作,否则直接跳过 10 { 11 sum=sum+num; //累加 12 if(num>0) //如果输入的数值大于0,则进行count1++运算 13 count1++; //正数个数统计 14 eIse //如果输入的数值小于0,则进行count2++运算 15 count2++; //负数个数统计 16 cin>>num; 17 } 18 if((count1+count2)!=0) //判断正数之和和负数之和是否等于0,如果不为0则进行如 下操作。如果为0则进行下方eIse处的操作 19 { 20 ave=sum/(count1+count2);//求平均
21 cout<<"和为:"<<sum<<'\t'<<"平均值为:"<<ave; 22 cout<<"\n正整数有"<<count1<<"个.\n"<<"负整数有"<<count2<<"个.\n"; 23 } 24 eIse //如果count1和count2之和为0,进行下操作 25 cout<<"没有输入有效的数!"; 26 return 0; 27 }
【代码详解】
第5和第6行首先定义一个变量并且赋初值。关于sum和ave为什么要定义成double型,大家可以上机试试。如果把它们定义成整型,最后的结果会有什么变化?第8行先读入一个数,然后才能开始循环,在循环里先累加刚刚读入的数。
第9行到第17行是循环体,重复累加和判断。如果是正整数,count1加1;如果是负整数,count2加1。
第18行是判断刚才有无计数。如果计数器未计数,那“count1+count2”为0,用累加和去除以0会出现错误。由此可以看出,我们在写程序时要尽量考虑周全,否则会发生很多意想不到的错误。
第20至第22行求平均并输出结果。
第25行输出没有输入有效数字的提示。
提示
我们在做累加和累乘运算时,运算结果可能会很大,如果定义成整型变量可能会超出范围,所以如果对结果的范围无法保证不超出整型数的范围,最好定义成双精度型或长整型。如本例的sum变量,求平均时,如果是两个整型变量相除,商自动为整型,如6/4结果为1。为了保证精度,本例把sum和ave变量都定义成双精度型变量。
【运行结果】
单击工具栏中的【Build】按钮,再单击【Execute Program】按钮,即可在控制台中运行程序。根据提示在命令行中输入若干整数,按【Enter】键,即可得到如图所示的运行结果。
【范例分析】
我们首先分析一下解决这个问题需要用到几个变量。首先读入的整数需要一个变量,此处我们用num。另外,总和和平均值各需要一个变量,此处用sum存放总和,用ave存放平均值。程序读入数据是重复的动作,显然要用到循环语句。在这里循环次数不固定,故适宜用while循环,循环结束条件是输入的数据为0,此时不需要用到循环控制变量。那么还需要什么变量?求平均该怎么求?肯定是用总和除以数的个数,显然我们在输入的过程中一定要统计输入的变量个数,所以还需要两个变量分别存放正整数的个数和负整数的个数,此处用count1和count2。
求和怎么求?用累加的方法,读入一个往存放和的变量中加一个。那么作为存放和的这个变量我们给它取个名字,叫累加器。
累加器既然是用来统计总数的,那么在统计之前一定要把现在可能的东西清理干净,也就是要给它赋初值0。
给累加器赋初值0,我们称为累加器清0。
本例中sum用来放和,当然是累加器,count1和count2用来统计数的个数,也是累加器,所以在使用之初都应该清0。
举一反三,如果一个变量被用来存放几个数的积,我们称它为累乘器。累乘器在使用之前必须赋初值1,这样才不会改变真正的待求的几个数的积。
给累乘器赋初值1,我们称为累乘器置1。
在这里还要考虑的一种特殊情况是,如果第1个数就是0,那么就不算平均,所以可以给ave也赋初值0。
提示
在此处将sum和ave定义成一个实型变量,否则平均数是一个取整后的整数。C++ 累加器在使用之前要清0,累乘器在使用之前要置1。
【拓展训练】
编写程序,从键盘读入个数不确定的整数,求出读入的数的个数和它们的积,0不参与计数。当输入为0时,程序结束。
3.5 综合应用
本节视频教学录像: 5分钟
在前面几节中已经学习了常量和变量等内容,本节运用这些知识来编写一个有趣、实用的程序。
【范例3-8】 计算贷款支付额。
本例演示如何编写程序来计算贷款支付额。贷款可以是购车款、学生贷款,或者是房屋抵押贷款。本程序要求用户输入利率、贷款年数和贷款总额,程序计算月支付金额和总偿还金额,并将它们显示出来。
计算月支付额的公式如下。
月支付额=(贷款总额×月利率)/(1-1/(1+月利率)年数×12)
在实际应用中不需要知道这个公式是如何推导出来的,只需给定年利率、年数和贷款总额,就能够算出月支付额。
⑴ 在Visual C++ 6.0中,新建名为“computeLoan”的【C++ Source File】源文件。
⑵ 在编辑窗口中输入以下代码(代码3-8.txt)。
01 #incIude<iostream> 02 #incIude<math.h> //用到其中的函数pow() 03 using namespace std; 04 int main() 05 { 06 int year; //贷款年数定义为整型 07 doubIe annuaIRate; //定义年利率变量 08 doubIe IoanSum; //定义贷款总额变量 09 doubIe monthRate; //定义月利率变量 10 doubIe totaIPay; //定义总支付额变量 11 doubIe monthPay; //定义月支付额变量 12 cout<<"\n请输入年贷款利率,如5.75:"; 13 cin>>annuaIRate; //输入年利率 14 cout<<"\n请输贷款年数,如15:"; 15 cin>>year; //输入贷款年数 16 cout<<"\n请输贷款总额,如100000:"; 17 cin>>IoanSum; //输入贷款总额 18 monthRate=annuaIRate/(12*100); //计算月利率 19 monthPay=IoanSum*monthRate/(1-1/pow(1+monthRate,year*12)); //计算月还款 20 totaIPay=monthPay*12*year; //计算还款总额 21 cout<<"\n你每月必须偿还:"<<monthPay<<"元!"; //输出月还款 22 cout<<"\n你一共需偿还"<<totaIPay<<"元。!"<<endI; //输出还款总额 23 return 0; 24 }
【运行结果】
编译、连接、运行程序,根据提示输入内容,即可在命令行中输出如图所示的结果。
【范例分析】
编写本程序的步骤如下。
⑴ 提示用户输入年利率、年数和贷款总额。
⑵ 利用年利率算出月利率。
⑶ 通过前面给出的公式计算月支付额。
⑷ 计算总支付额,它是月支付额乘以12再乘以年数。
⑸ 显示月支付额和还款总额。
通过上面的分析,本例需要用到以下变量。
年利率:annualRate
贷款年数:year
贷款总额:loanSum
月利率:monthRate
总支付额:totalPay
月支付额:monthPay
计算xy用到头文件math.h中的函数pow(),函数原型如下。
double pow(double x,double y);
3.6 实战练习
1. 操作题
在C++中编写应用程序,模仿书中例子尝试完成有下列要求的程序。
⑴ Jack生病了,医生说他发烧到华氏101度,他自己以为病得很严重。请将其体温转换为摄氏温度,并告诉他病得严重吗?摄氏度=(5/9)×(华氏度-32)。
⑵ 通过键盘输入5个学生的成绩,然后求取5个学生的平均成绩。
2. 思考题
可不可以通过某种方式在第⑵题里面实现这样的功能:当输入学生成绩数目为5时,实现自动输出操作。