4.2 数据基本类型

数据类型是不同形式的信息在内存中分配方式的基本约定,是构建程序的基础。在C++语言中定义了很多的数据类型,本节将介绍常用的预定义数据类型有字符类型、整数类型、浮点数类型、布尔类型、无类型和宽字符类型等,以及这些类型的基本用法,C++的基本类型如图4-3所示。

图4-3 数据基本类型

4.2.1 整数类型(int)

在C++中,整型数据即为整数,是不包含小数部分的数值型数据。整型数据分为长整型(long int)、一般整型(int)和短整型(short int)。在int前面加long和short分别表示长整型和短整型。

整型数据的存储方式为按二进制数形式存储,例如十进制整数89的二进制形式为1011001,其在内存中的存储形式如图4-4所示。

图4-4 整型数据的储存形式

在整型符号int和字符型符号char的前面,可以加修饰符signed(表示“有符号”)或unsigned(表示“无符号”)。如果指定为signed,则数值以补码形式存放,存储单元中的最高位(bit)用来表示数值的符号。如果指定为unsigned,则数值没有符号,全部二进制位都用来表示数值本身,如图4-5所示。

图4-5 有符号与无符号数据的储存形式

有符号时,能存储的最大值为215-1,即32 767,最小值为-32 768。无符号时,能存储的最大值为216-1,即65535,最小值为0。有些数据是没有负值的,可以使用unsigned,它存储正数的范围比用signed时要大一倍。整型数据的取值范围见表4-4。

表4-4 整型数据的类型说明以及取值范围

注意:表中类型标识符一栏中,方括号[ ]包含的部分可以省写,如short和short int等效,unsigned int和unsigned等效。

4.2.2 字符类型(char)

字符类型数据是用一对单引号括起来的一个字符,单引号只是字符与其他部分的分割符,不是字符的一部分。

例如:

并且,不能用双引号代替单引号。在单引号中的字符不能是单引号或反斜杠。

另一种表示字符常量的方法是使用转义字符。C++规定,采用反斜杠后跟一个字母来代表一个控制字符,具有新的含义。

C++中常用的转义字符见表4-5。

表4-5 C++中常见的转义字符

【例4-1】编写程序,输出字符型数据。

(1)在Visual Studio 2017中,新建名称为“4-1.cpp”的Project1文件。

(2)在代码编辑区域输入以下代码。

【程序分析】本程序定义两个字符型变量a、b,并初始化赋值,输出两个字符。在cout语句中输出三个转义字符。

在Visual Studio 2017中的运行结果如图4-6所示。

图4-6 字符类型

4.2.3 宽字符类型(wchar_t)

char类型的常见编码方式是ASCII。ASCII编码是一种基于8位二进制数的字符编码算法,是美国ANSI制定的一种单字符编码方案,能表示256种可能的字符,常见的字母、符号、键盘指令等,全能用ASCII码表示,而由于ASCII码是基于8位的编码,因此用这种算法的编译器,char类型都占8位。

注意:因为用了ASCII,所以char才是8位,而不是char是8位,所以采用ASCII。

wchar_t的出现,是出于程序兼容多语言的需求,因为在很多语言中,字符的数量远远大于256,因此需要把原字符进行扩容,以便能表示更多的字符类型。wchar_t全称是wide character type,也就是宽字符。

标准C++中的wprintf()函数以及iostream类库中的类和对象能提供wchar_t宽字符类型的相关操作。

4.2.4 浮点数类型

浮点数类型变量又称为实型,它是由整数部分和小数部分组成的。实型变量用于存储实型数值的变量,根据精度可分为单精度类型、双精度类型以及长双精度类型3种。

浮点数类型数据的类型说明以及取值范围见表4-6。

表4-6 浮点型数据的类型说明以及取值范围

1. 单精度类型

单精度类型使用关键字float表示,它占有4字节内存,取值范围为-3.4*10-38~3.4*1038。单精度类型变量的定义语法如下:

float  变量名;

例如,定义一个单精度类型变量a,初始化为1.11,语法如下:

    float  a=1.11f;

2. 双精度类型

双精度类型使用关键字double表示,它占有8字节内存,取值范围为-1.7*10-308~1.7*10308。双精度类型变量的定义语法如下:

double  变量名;

例如,定义一个双精度类型变量b,初始化为2.234,语法如下:

    double  b=2.234;

3. 长双精度类型

长双精度类型使用关键字long double表示,它占有8字节内存,取值范围为-1.7*10-308~1.7*10308。长双精度类型变量的定义语法如下:

long  double  变量名;

例如,定义一个长双精度类型变量c,初始化为3.345,语法如下:

    long  double  c=3.345;

4.2.5 布尔类型(bool)

布尔类型只有两个值false和true。布尔类型通常用来判断条件是否成立,如果变量值为0表示假,并且可以赋予文字值false;变量值为非0表示真,可以赋予文字值true。

【例4-2】编写程序,定义bool类型的变量,并对其进行判断。

(1)在Visual Studio 2017中,新建名称为“4-2.cpp”的Project2文件。

(2)在代码编辑区域输入以下代码。

【程序分析】本例中定义了一个bool类型的变量x,并赋值为2。因为2是非零值,所以语句“bool x=2;”等效于“bool x=true;”。因此if语句表示,x非零则输出“正确”。执行表达式“x=x-1;”语句时,bool类型的数据true参与算术运算,其值会转为一个整数值1,所以减1后结果为0,赋给x时会转换为bool值false。执行if语句时,x的值是0。所以不输出“错误”。

在Visual Studio 2017中的运行结果如图4-7所示。

图4-7 bool类型变量

当表达式需要一个算数值时,布尔类型对象将被隐式地转换成int类型也就是整型对象,false就是0,true就是1。

【例4-3】编写程序,输出bool类型变量的值。

(1)在Visual Studio 2017中,新建名称为“4-3.cpp”的Project3文件。

(2)在代码编辑区域输入以下代码。

【程序分析】本例定义bool类型的变量x和y,分别赋值为true和false,并输出它们的值。

在Visual Studio 2017中的运行结果如图4-8所示。

图4-8 bool类型

4.2.6 无类型(void)

无类型就是空类型,放在函数前面,表示该函数不返回任何值,放在函数参数部分,表示函数不传入参数。

例如:

注意:void不能代表一个真实的变量。

下面代码都企图让void代表一个真实的变量,因此都是错误的代码:

空类型在调用函数值时,通常应向调用者返回一个函数值。这个返回的函数值是具有一定的数据类型的,应在函数定义及函数说明中给以说明,但是,也有一类函数,调用后并不需要向调用者返回函数值,这种函数可以定义为“空类型”。其类型说明符为void。

之所以需要空类型,是因为函数的默认返回值类型是int,如果在函数定义时未带返回类型说明,则默认为int;即使函数中没有return语句,编译器按照函数返回值的原理,会返回一个不确定的值。如果将这样的函数错用在表达式里,语法上没错,但会带来很难察觉的逻辑错误。而将空类型函数用在表达式里是一个编译错误。

4.2.7 对齐

现代计算机中内存空间都是按照字节划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定变量的时候经常在特定的内存地址访问,这就需要各类型数据按照一定的规则在空间上排列,而不是顺序地一个接一个地排放,这就是对齐。32位机器上各数据类型的长度为:char为1字节、short为2字节、int为4字节、long为4字节、float为4字节、double为8字节。

1. 对齐的意义

在不同编译平台或处理器上,字节对齐会造成消息结构长度的变化。编译器为了使字节对齐可能会对消息结构体进行填充,不同编译平台可能填充为不同的形式,这样会大大增加处理器间数据通信的风险。所以对于本地使用的数据结构,为提高内存访问效率,采用四字节对齐方式;同时为了减少内存的开销,会合理安排结构体成员的位置,减少四字节对齐导致的成员之间的空隙,提高内存的存取效率。如果在不同平台之间传递二进制流,那么在这两个平台间必须要定义相同的对齐方式,不然莫名其妙地出了一些错,可是很难排查的。

2. 对齐的实现

通常,在写程序的时候,不需要考虑对齐问题,编译器会自动选择适合目标平台的对齐策略。当然,也可以通知给编译器传递预编译指令而改变对指定数据的对齐方法,比如写入预编译指令#pragma pack(),即告诉编译器按两字节对齐。

整个程序在给每个变量进行内存分配时都会遵循对齐机制,也都会产生内存空间的浪费。但是,这种浪费是值得的,因为它换来的是效率的提高。