学习一门编程语言,首先要掌握它的数据类型。不同的数据类型占用的内存空间不同,合理定义数据类型可以优化程序的运行。本章将介绍C++中常见的数据类型及数据的输入与输出格式。
本章知识架构及重难点如下:
先来看一个简单的C++程序,其作用是在屏幕上输出“Hello World!”。
程序输出结果如图2.1所示。
在这个简单的C++程序中,出现了包含命令、头文件、命名空间、主函数、输出流语句、字符串常量、注释等概念,一起来认识下。
图2.1 程序输出结果
1.包含命令(#include)、头文件
C++程序中,带“#”号的语句称为预编译命令,该命令会在程序编译之前先进行。include的含义是“包含”和“引用”,#include命令则为包含头文件命令,表示本程序将嵌入指定的头文件。
所谓头文件,指的是程序中用于声明函数、变量、宏等的文件,通常以.h为后缀名。使用头文件(包括系统头文件和自定义头文件)可以方便地引用已写好的代码,提高代码的复用性和可维护性,同时降低代码的耦合度,使代码更加模块化。
因此,第一行代码“#include <iostream>”表示包含C++标准输入输出流头文件iostream.h,也就是说,程序编译时会在此处将iostream.h头文件中的内容展开。
2.命名空间(namespace)
一个中大型软件往往由多名程序员共同开发,会使用大量的常量、变量、函数、结构体、枚举、类等,因此不可避免地会出现命名冲突。工程越大,名称互相冲突性的可能性越大。为了解决合作开发时的命名冲突问题,C++引入了“命名空间”的概念。在不同的命名空间里,同名变量放在一起编译不会出现问题。
因此,第二行代码“using namespace std;”表示使用C++标准库定义的std命名空间。
3.主函数main()
第3~6行为main()函数。main()函数又称为主函数,是C++程序执行的入口。程序将从main()函数的第一条指令开始执行;main函数结束,整个程序也执行结束。其中,main为函数名,“()”里可放置参数。
其中,“int main()”是main函数的函数头,表示main()函数将返回一个整型数值;大括号“{ }”中的内容是需要执行的,称为函数体。函数体中,各行代码将按照先后顺序依次执行,写在前面的先执行,写在后面的后执行。
Visual C++中通常使用void main(),但DEV C++中必须使用int main(),否则会报错。
4.输出流语句
第5行代码“cout << "Hello World!\n";”表示通过数据输出流,输出英文“Hello World!”。其中,双引号" "代表该语句是字符串常量,cout表示输出流,<<表示将字符串传送到输出流中。
C语言中,我们使用scanf()、printf()函数进行数据的输入输出操作。C++兼容C语言,因此这两个格式化输入输出函数仍然可以使用。但C++提供了一套全新的、更容易使用的输入输出库,即使用数据输入输出流和cout<<、cin>>进行输入、输出。
5.注释
代码中使用了多处“//”注释,对本行代码进行解释说明。编译器不会对注释部分进行编译。
C++程序中有两种注释方法:单行注释“//”和多行注释“/* */”。
“//”是单行注释,从注释符号“//”开始,到本行代码结束的内容为注释内容,一般用于对本行代码或下面的代码进行解释说明。
“/* */”是多行注释,“/*”放在注释内容前,“*/”放在注释内容后,“/*”和“*/”中间的内容为注释部分。注意,多行注释中不允许嵌套另一个多行注释。
关键字(keywords)又称为保留字,是C++中规定的具有特定意义的字符串,如表2.1所示。用户定义的常量、变量、函数等名称不能与关键字相同,否则会出现错误。
表2.1 C++保留关键字
这些关键字不需要背诵。在C++开发环境中编写代码时,保留关键字会显示为特殊格式,如变成蓝色。后续学习中,我们会逐渐熟悉这些关键字的具体用法,这里不需要死记硬背。
标识符是对C++程序中常量、变量、语句标号以及用户自定义函数等名称进行标识的符号。其命名规则如下:
标识符必须由字母、数字及下画线组成,且不能以数字开头。
C++区分大小写,因此大写和小写英文字母代表的是不同的标识符。
标识符不能是系统关键字。
标识符应体现一定的功能含义,便于理解。通常采用所表述内容的英文、拼音等,力求“见名知义”,同时应受一定规范的约束。
例如,6A、ABC*、int是不合法的标识符。其中,6A以数字开头,ABC*使用了符号“*”,int是系统关键字。mingri和MingRi是不同的标识符,因为C++区分大小写。同理,int和Int含义不同,Int不再是系统关键字,而是合法的标识符。表示长方体的长、宽、高,使用length、width、height标识符远比使用a、b、c表示更易于理解。
C++包括整型、浮点型、字符型、布尔型等基本数据类型,数组、结构体、共用体等构造数据类型,指针类型,以及用户自定义数据类型。C++能够实现复杂的数据结构,还可以定义类,实现面向对象编程。首先来认识下C++中的基本数据类型,如图2.2所示。
图2.2 C++基本数据类型
程序运行过程中,其值不能改变的量称为常量。常量包括整型常量、浮点型常量、字符常量、字符串常量等。
例如,下面通过cout向屏幕输出4行内容。cout是输出流,可向屏幕输出不同类型的数据。2009是整数(即整型常量),2.14是小数(即浮点型常量),'a'是字符常量,"Hello World"是字符串常量。
整型常量就是整型常数(没有小数部分),如123、−456、0等。编写代码时,可将整型常量表示为十进制、八进制和十六进制3种表示形式。
十进制:我们日常见到的整数都是十进制形式的,如365、75等。十进制数使用0~9来表示所有数,递进关系为逢十进一。注意,除了0本身,其他十进制数不能以0开头。
八进制:使用0~7来表示所有的数,递进关系为逢八进一。八进制常量前要加上0作为前缀,如0123、0432等。
十六进制:使用0~9和字母A~F(或a~f)来表示所有的数,递进关系为逢十六进一。十六进制常量前要加上0x或0X作为前缀,如0x123、0x3ba4。
例如,0396不是合法的八进制整数,因为9不是八进制应有的取值;0X3N不是合法的十六进制整数,因为N不是十六进制应有的取值。
整型常量不可以无限大,它的最大值是有限定的。根据CPU寄存器位数以及编译器的不同,最大的整型常量值也会不同。
“合法”主要指能通过编译器编译,“非法”或“不合法”指不能通过编译器编译。
浮点型(又称为实型)常量就是我们常见的小数,只能采用十进制形式表示。其表示方法有两种,即小数表示法和指数表示法。
1.小数表示法
小数表示法由整数部分和小数部分组成,中间用小数点分隔。例如,0.0、2.25、0.00596、5.0、536.、-5.3、-0.002均为合法的实型常量。
另外,整数部分和小数部分可以省略一处,例如,.2表示小数0.2,2.表示小数2.0。
2.指数表示法
指数表示法又称为科学记数法,指数部分以“e”或“E”开始,后跟整数。
例如,1.2e20和-2.4e-2是合法的实型常量,其中1.2e20表示1.2×10 20 ,-2.4e-2表示-2.4×10 -2 。而E5、3E2.5不是合法的实型常量,因为E5中E之前无数字,3E2.5中E后面有小数。
在字母e(或E)之前的小数部分中,小数点左边应有且只有一位非零的数字,称为规范化的指数形式。
使用一对单直撇引号(' ')括起来的字符就是字符常量,如'a'、'A'、'?'、'#'、'b'等。编译代码时,编译器会根据ASCII码表将字符常量转换成整型常量。其中,'a'对应的ASCII码值是97,'A'对应的ASCII码值是65,'?'对应的ASCII码值是63。' '表示空字符(NULL),有的编译器把它编译成0,有的则编译成其他值。
除了这些固定字符,ASCII码表中还有很多无法通过键盘输入的字符。例如,2.1节示例代码中的“\n”符号,输出结果中却不显示该符号,只是进行了换行操作。这种符号称为转义字符。
表2.2 常用的转义字符
下面来看一个转义字符的应用示例,代码如下:
代码运行结果如图2.3所示。
图2.3 示例运行结果
字符串常量是用一对双直撇引号(" ")括起来的字符序列。例如,"hello"、"welcome to China"都是合法的字符串常量。其中," "表示一个空字符串,此时字符串的长度为0。
字符串常量实际上是一个字符数组,可以将字符串分解成若干个字符,字符的数量是字符串的长度。字符串常量一般用来给字符数组变量赋值或是直接作为实参传递,为告知编译器字符串已经结束,一般在给字符数组赋初值时在字符串的末尾加上字符'\0',表示字符结束,如果不加字符结束标志,可能会出现意想不到的错误。
字符'A'与字符串"A"含义不同。内存中,字符'A'则只包含一个字符;字符串"A"由'A'和'\0'两个字符组成,字符串的长度是2。
除了整型、浮点型、字符型、字符串型常量外,还存在布尔常量、枚举常量和宏定义常量等。
布尔常量:布尔类型(bool)是表示是否、对错等真假判断的数据类型,常用在逻辑判断中。布尔类型只有true和false两个取值。true表示真,其值为1;false表示假,其值为0。
枚举常量:枚举型数据中定义的数据也都是常量。
宏定义常量:通过#define宏定义的值也是常量。例如:
#define PI 3.1415
其中PI就是常量,代表的是3.1415。编译时遇到PI,就会被自动替换为3.1415。
变量是指在程序运行期间其值可以发生改变的量。每个变量都必须有一个名称作为唯一的标识,且具有一个特定的数据类型。变量使用之前,一定要先进行声明或定义。
C++中,变量声明是指为变量提供一个名称,并告诉编译器这个变量将被使用,但不会为其分配内存空间。变量声明的一般形式如下:
数据类型 变量名;
其中,“数据类型”指变量的类型,如整型、浮点型、字符型等,“变量名”则是用户给变量起的名称,必须符合标识符的命名规则。
多个同一类型的变量可以在一行中声明,变量名之间用逗号隔开。例如:
int x; int a,b,c;
定义变量时,需要为其分配一块内存空间,以存储其值。语法形式如下:
数据类型 变量名=初始值;
例如,下面定义了多个变量:
int x=10; int a=,b=2,c=3;
定义多个赋相同初值的变量时,应写成“int a=3, b=3, c=3;”形式,不能写成“int a=b=c=3;”形式。
整型变量就是用来存储整型数值的变量。
根据占有的内存空间大小,整型变量可分为基本整型(int)、短整型(short)和长整型(long)3类。根据是否有符号,还可分为有符号整型(signed)和无符号整型(unsigned)两类。因此,整型变量共分为6类,如表2.3所示。其中,方括号中的关键字可以省略。
表2.3 整型变量的分类
不同的整型,其在内存中占用的字节空间不相同,因此可表述的变量数值范围也不同。以32位操作系统为例,短整型、整型、长整型变量占用的字节数和可表示的数值范围如表2.4所示。
表2.4 整型变量可表示的数值范围
例如,下面定义了一个整型变量a,为它分配了4个字节的内存空间,并设初始值为10。
int a=10;
变量赋值时,整型常量后可以加上L或l、U或u等后缀,清晰指明其类型,如1314L、520U等。
根据CPU寄存器位数和编译器的不同,最大的整型数值也会不同。32位操作系统中,基本整型(简称为整型)为4字节,长整型为4字节;64位操作系统中,基本整型为4字节,长整型为8字节。
C++程序中,布尔型(bool)被当作整型对待,false表示0,true表示1。因此,将布尔型赋值给整型是合法的,将整型赋值给布尔型也是合法的。例如:
浮点型变量分为单精度(float)、双精度(double)和长双精度(long double)3类,其占用的字节数和可表示的数值范围如表2.5所示。
表2.5 实型变量可表示的数值范围
float和double相比,double类型的变量具有更高的精度,即它可以表示更多的小数位数。float保留到小数点后7位,有效数字为6~7位;double保留到小数点后16位,有效数字为15~16位。实际开发中,一般多使用double类型,尽可能地避免精度损失。
例如,下面代码声明了多个浮点型变量。
float a; double b; long double c;
在程序中使用浮点型数据时,需要注意以下两点。
1.浮点型数据相加
浮点型数据的有效数字是有限制的,如float的有效数字是6位或7位,如果将数字86041238.78赋值给float类型,显示的数字可能是86041240.00,个位数8被四舍五入,小数位被忽略。如果将86041238.78与5相加,输出的结果为86041245.00,而不是86041243.78。
2.浮点型数据与0进行比较
在开发程序的过程中,经常会进行两个浮点型数据的比较,此时尽量不要使用“==”或“!=”运算符,而应使用“>=”或“<=”之类的运算符。
例如,下述代码直接将浮点型变量与0进行比较,不是高质量的代码。如果程序要求的精度非常高,可能会产生未知的结果。
通常的做法是:定义0的精度,然后判断浮点数是否在该精度范围内。例如:
程序运行结果如图2.4所示。
图2.4 程序运行结果
数字里可以有下画线,这些下画线不会影响数字的值,仅提供分隔作用,方便阅读。下画线不能写在数字开头或末尾,二进制和十六进制前缀字母旁,以及小数点旁。
变量的值是动态改变的,每次改变都需要进行赋值。变量赋值的形式如下:
变量名=表达式;
其中,表达式由运算符、操作数、括号等组成。最简单的表达式就是一个数。
我们在2.5.1节中已经学习过,声明变量时可以把数值赋给变量,这个过程叫变量赋初值。除此以外,还可以先声明变量,再为其赋值。例如,下面的代码先声明整型变量i,然后将常量100赋值给i。
例如,下面的代码声明了3个整型变量i、j、k,先为变量i、j赋值,再将i+j的值赋给k。
char关键字用来定义字符型变量,其在内存中占用1个字节。例如:
字符型变量在内存中存储的是字符的ASCII码,即一个无符号整数。其形式与整型变量的存储形式一样,因此字符型数据与整型数据之间可以通用。也就是说:
(1)一个字符型数据,既可以字符形式输出,也可以整数形式输出。
(2)允许对字符数据进行算术运算,即对它们的ASCII码值进行算术运算。
给char型变量赋值时不能使用3个单引号,否则编译器会无法识别开始和结束位置。例如:
char cChar='A''; /*使用3个单引号为字符型赋值*/
上述代码编译时会提示“error c2001:newline in constant”错误。
【实例2.1】 字符型数据进行算术运算。 (实例位置:资源包\TM\sl\2\1)
本实例中,定义两个字符型变量并赋值,一个字符进行减32计算,另一个字符进行加32计算,最后这两个字符分别进行加10计算,并通过格式化输出函数printf()以%d和%c格式输出。代码如下:
程序运行结果如图2.5所示。
编程训练(答案位置:资源包\TM\sl\2\编程训练\)
【训练1】字符B的无中生有 程序中不出现字符“B”,试着输出字符“B”。
【训练2】预测“B+32”的结果 以字符格式%c输出“'A'+32”的结果,观察结果,猜测一下“'B'+32”的结果。
图2.5 字符型数据进行算术运算
在用户与计算机进行交互的过程中,数据输入与输出是必不可少的操作过程。计算机需要通过输入获取用户的操作指令,并通过输出显示操作结果。本节将介绍数据输入与输出的相关内容。
C++中,数据的输入和输出发生在标准输入/输出设备(即键盘和显示器)、外部存储介质(即磁盘文件),以及内存空间之间。因此,对键盘和显示器的输入/输出简称“标准I/O”,对磁盘文件的输入/输出简称“文件I/O”,对内存空间的输入/输出简称“串I/O”。
C++把数据之间的这种传输操作称为“流”,意思是数据传输过程像水一样从一个地方流到另一个地方,实现输入的为输入流,实现输出的为输出流。流既可以表示数据从内存传送到某个载体或设备中,也可以表示数据从某个载体或设备传送到内存缓冲区中。程序用流统一对各种计算机设备和文件进行操作,使程序与设备、文件无关,提高了程序设计的通用性和灵活性。
C++定义了ios基类,以及由其派生的输入流类istream和输出流类ostream。标准I/O操作有4个类对象,分别是cin、cout、cerr和clog。其中,cin代表标准输入设备(即键盘),也称cin流或标准输入流;cout代表标准输出设备(即显示器),也称cout流或标准输出流。当进行键盘输入操作时,使用cin流;当进行显示器输出操作时,使用cout流;当进行错误信息输出操作时,使用cerr流或clog流。
C++数据流通过重载运算符“>>”和“<<”执行输入和输出操作。输出操作使用左移运算符“<<”向流中插入一个字符序列,输入操作使用右移运算符“>>”从流中提取一个字符序列。
1.cout语句
cout语句的一般格式为:
cout<<表达式1<<表达式2<<…<<表达式n;
cout代表显示器,执行cout << x操作就相当于把x的值输出到显示器。
先把x的值输出到显示器屏幕上,在当前屏幕光标位置显示出来,然后cout流恢复到等待输出的状态,以便继续通过插入操作输出下一个值。当使用插入操作向一个流输出某个值后,再输出下一个值时将被放在上一个值的后面,所以为了让流中前后两个值分开,可以在输出一个值后接着输出一个空格,或一个换行符,或其他需要的字符或字符串。
一个cout语句可以分写成若干行。例如,下面的语句:
cout<< "Hello World!" <<endl;
可以简单地写成多行:
也可写成多个cout语句:
2.cin语句
cin语句的一般格式为:
cin>>变量1>>变量2>>…>>变量n;
cin代表键盘,执行cin>>x操作就相当于把键盘输入的数据赋给变量x。
通过键盘输入数据时,只有输入完数据并按下Enter键后,系统才会把该行数据存入键盘缓冲区,供cin流顺序读取给变量。另外,从键盘上输入的每个数据之间必须用空格或Enter键分开,因为cin为一个变量读入数据时是以空格或Enter键作为其结束标志的。
当cin>>x操作中的x为字符指针类型时,则要求从键盘的输入中读取一个字符串,并把它赋值给x指向的存储空间。若x没有事先指向一个允许写入信息的存储空间,则无法完成输入操作。另外,从键盘上输入的字符串,其两边不能带有双引号定界符,若有则只作为双引号字符看待。对于输入的字符也是如此,不能带有单引号定界符。
例如,下面的代码可将用户输入的数打印出来。其中,endl用于向流的末尾部位加入换行符。
【实例2.2】 默写王之涣的《登鹳雀楼》。 (实例位置:资源包\TM\sl\2\2)
使用cout向控制台输出唐朝诗人王之涣的《登鹳雀楼》,具体代码如下:
运行程序,将向控制台屏幕输出诗句,效果如图2.6所示。
图2.6 向控制台屏幕输出诗句
1.流输出(cout)
cout是输出流类ostream的对象,通过其中的成员函数可对数据进行格式化输出,如表2.6和表2.7所示。
表2.6 ostream类的成员函数
表2.7 格式化常量
这些成员函数使用时,前面要加上cout对象名,如cout.setf(ios::left)、cout.width(5)等。另外,可以同时设置多种格式,不同格式间用“|”连接。例如,数据输出时要求左对齐且字母大写,格式应为cout.setf(ios::left|ios::uppercase)。
另外,C++标准库提供的iomanip.h头文件中包含了大量的格式控制符,如表2.8所示,可以直接使用它们进行数据格式化输出,更加简单、快捷。仔细观察,读者会发现这些格式控制符和表2.7中的格式化常量非常相似,但可设置的格式更多。
表2.8 C++格式控制符
【实例2.3】 变幻莫测的整数。 (实例位置:资源包\TM\sl\2\3)
本实例中,定义一个整型变量并赋值,利用不同形式输出整型,具体代码如下:
程序运行结果如图2.7所示。
【实例2.4】 十六进制与十进制转换。 (实例位置:资源包\TM\sl\2\4)
本实例中,定义两个整型变量,一个为十六进制整数,另一个为十进制整数。利用cout输出第一个变量的十进制、十六进制形式,第二个变量的小写十六进制和大写十六进制形式。代码如下:
程序运行结果如图2.8所示。
图2.7 整数输出
图2.8 十进制和十六进制转换
【实例2.5】 控制输出精确度。 (实例位置:资源包\TM\sl\2\5)
本实例中,定义一个整型变量并赋值,定义一个双精度变量并赋值,利用cout输出这两个不同精度的格式,具体代码如下:
程序运行结果如图2.9所示。
【实例2.6】 千变万化的小数。 (实例位置:资源包\TM\sl\2\6)
本实例中,定义两个单精度类型,用cout输出不同长度的小数,具体代码如下:
程序运行结果如图2.10所示。
图2.9 控制输出精确度
图2.10 流输出小数控制
2.printf()函数输出
C++中保留了C语言的输出函数printf(),使用它可将任意数量、类型的数据输出到屏幕中。printf()函数的声明形式如下:
printf("[控制格式]... [控制格式]...",数值列表);
其中,“数值列表”为待输出数值,可以是多个数值,相邻数值间用逗号隔开;“控制格式”表示数值输出的格式,其数量与待输出数值一致,并一一对应。
控制格式的以“%+特定字符”,形式如下:
%[*][域宽][长度]类型
其中,“*”代表可以使用占位符;“域宽”表示输出长度,如果输出内容没有域宽长,用占位符占位;如果比域宽长,按实际内容输出,以适应域宽;“长度”决定输出内容的长度。例如,%d表示以整型格式输出数据,%c表示以字符格式输出数据。
常见的格式输出类型及输出方式如表2.9所示。
表2.9 printf()函数常见格式输出类型
【实例2.7】 整数的格式控制输出。 (实例位置:资源包\TM\sl\2\7)
本实例中,使用printf()函数输出整型数,在格式控制时分别以空格和“0”为占位符。代码如下:
程序运行结果如图2.11所示。
【实例2.8】 字符串的格式控制输出。 (实例位置:资源包\TM\sl\2\8)
本实例中,定义字符型指针(这里不用理会,第7章中会详细介绍)保存字符串"helloworld",利用printf()函数输出,体会不同格式控制下的输出差异。代码如下:
程序运行结果如图2.12所示。
图2.11 整型数格式输出
图2.12 字符串格式输出
【实例2.9】 浮点数的格式控制输出。 (实例位置:资源包\TM\sl\2\9)
本实例中,定义单精度浮点数和双精度浮点数,利用printf()函数输出,体会不同格式控制下的输出差异。代码如下:
程序运行结果如图2.13所示。
【实例2.10】 科学计数法输出。 (实例位置:资源包\TM\sl\2\10)
本实例中,利用printf()函数和格式控制符%e,用科学计数法输出小数。代码如下:
程序运行结果如图2.14所示。
图2.13 浮点数格式输出
图2.14 科学计数法输出
%x、%X、%#x、%#X的区别:%x表示输出字母小写形式,%X表示输出字母大写形式,#表示输出标准十六进制形式。建议添加“#”,否则当输出的十六进制正好没有字母时,会被误认为是一个十进制数。
编程训练(答案位置:资源包\TM\sl\2\编程训练\)
【训练3】控制台输出汉字 使用cout向控制台输出汉字“明日科技”。
【训练4】地球的年龄有多大 地球的年龄约为45.5亿年,用科学计数法输出地球的年龄。
答案位置:(资源包\TM\sl\2\实践与练习\)
综合练习 1:输出狙击枪形状 利用字符串常量输出狙击枪形状,输出形状如下。(提示:利用搜狗特殊符号。)
综合练习 2:模拟超市结账 某人在超市挑选的商品价格分别为56.75元、72.91元、88.50元、26.37元和68.51元,计算他应支付的总金额。运行结果如下:
实际应付313.04元
综合练习 3:模拟缴纳电费 编写一个程序,先模拟输出电费账单,然后要求用户输入缴费金额(大于574元),输出当前账户的电费余额,输出结果如图2.15所示。
图2.15 实现效果
综合练习 4:数字与字母 输出整型数65对应的字母,再输出整型数97所对应的字母,最后根据二者的差值,推导大字母和小写字母的关系。