定义变量时,不可或缺的一个要素就是数据类型。本质上讲,这就是为了实现计算需求,我们必须先定义好数据的样式,告诉计算机这些数据占多大空间,这就是所谓“数据类型”的含义。
C++支持丰富的数据类型,它内置了一套基本数据类型,也为我们提供了自定义类型的机制。
接下来我们先介绍基本数据类型,主要包括算术类型和空类型(void)。其中算术类型又包含了整型和浮点型;而空类型不对应具体的值,只用在一些特定的场合,比如一个函数如果不返回任何值,我们可以让void作为它的返回类型。
1.整型
整型(integraltype)本质上来讲就是表示整数的类型。
我们知道在计算机中,所有数据都是以二进制“0”“1”来表示的,每个叫做一位(bit);计算机可寻址的内存最小单元是8位,也就是一个字节(Byte)。所以我们要访问的数据,都是保存在内存的一个个字节里的。
一个字节能表示的最大数是28=,这对于很多应用来讲显然是不够的。不同的需求可能要表示的数的范围也不一样,所以C++中定义了多个整数类型,它们的区别就在于每种类型占据的内存空间大小不同。
C++定义的基本整型包括char、short、int、long,和C++11新增的longlong类型,此外特殊的布尔类型bool本质上也是整型。
在C++中对它们占据的长度定义比较灵活,这样不同的计算机平台就可以有自己的实现了(这跟C是一样的)。由于char和bool相对特殊,我们先介绍其它四种。C++标准中对它们有最小长度的要求,比如:
lshort类型至少为16位(2字节)
lint至少2字节,而且不能比short短
llong至少4字节,而且不能比int短
llonglong至少8字节,而且不能比long短
现在一般系统中,short和long都选择最小长度,也就是short为16位、long为32位、longlong为64位;而int则有不同选择。我们一般使用的电脑操作系统,比如Windows7、Windows10、MacOS等等的实现中,int都是32位的。
所以short能表示的数有=个,考虑正负,能表示的范围就是-~;而int表示的数范围则为-~-1。(大概是正负20亿,足够用了)
#includeiostream
usingnamespacestd;
intmain()
{
shorta=1;
cout"a="aendl;
cout"a的长度为:"sizeof(a)endl;
intb;
cout"b的长度为:"sizeof(b)endl;
longc;
cout"c的长度为:"sizeof(c)endl;
longlongd;
cout"d的长度为:"sizeof(d)endl;
cin.get();
}
这里我们用到了sizeof,这是一个运算符,可以返回某个变量占用的字节数。我们可以看到,变量占用的空间大小只跟类型有关,跟变量具体的值无关。
2.无符号整型
整型默认是可正可负的,如果我们只想表示正数和0,那么所能表示的范围就又会增大一倍。以16位的short为例,本来表示的范围是-~,如果不考虑负数,那么就可以表示0~。C++中,short、int、long、longlong都有各自的“无符号”版本的类型,只要定义时在类型前加上unsigned就可以。
shorta=;
cout"a="aendl;
cout"a的长度为:"sizeofaendl;
unsignedshorta2=;
cout"a2="a2endl;
cout"a2的长度为:"sizeofa2endl;
上面的代码可以测试无符号数表示的范围。需要注意,当数值超出了整型能表示的范围,程序本身并不会报错,而是会让数值回到能表示的最小值;这种情况叫做“数据溢出”(或者“算术溢出”),写程序时一定要避免。
由于类型太多,在实际应用中使用整型可以只考虑三个原则:
一般的整数计算,全部用int;
如果数值超过了int的表示范围,用longlong;
确定数值不可能为负,用无符号类型(比如统计人数、销售额等);
3.char类型
如果我们只需要处理很小的整数,也可以用另外一种特殊的整型类型——char,它通常只占一个字节(8位)。不过char类型一般并不用在整数计算,它更重要的用途是表示字符(character)。
计算机底层的数据都是二进制位表示的,这用来表示一个整数当然没有问题,可怎么表示字母呢?这就需要将常用的字母、以及一些特殊符号对应到一个个的数字上,然后保存下来,这就是“编码”的过程。
最常用的字符编码集就是ASCII码,它用0~表示了个字符,这包括了所有的大小写字母、数字、标点符号、特殊符号以及一些计算机的控制符。比如字母“A”的编码是65,数字字符“0”的编码是48。
在程序中如果使用char类型的变量,我们会发现,打印出来就是一个字符;而它的底层是一个整数,也可以做整数计算。
charch=65;
cout"65对应的字符为:"chendl;
charch2=ch+1;
cout"66对应的字符为:"ch2endl;
char类型用来表示整数时,到底是有符号还是无符号呢?之前的所有整型,默认都是有符号的,而char并没有默认类型,而是需要C++编译器根据需要自己决定。
所以把char当做小整数时,有两种显式的定义方式:signedchar和unsignedchar;至于char定义出来的到底带不带符号,就看编译器的具体实现了。
另外,C++还对字符类型进行了“扩容”,提供了一种“宽字符”类型wchar_t。wchar_t会在底层对应另一种整型(比如short或者int),具体占几个字节要看系统中的实现。
wchar_t会随着具体实现而变化,不够稳定;所以在C++11新标准中,还为Unicode字符集提供了专门的扩展字符类型:char16_t和char32_t,分别长16位和32位。
4.bool类型
在程序中,往往需要针对某个条件做判断,结果只有两种:“成立”和“不成立”;如果用逻辑语言来描述,就是“真”和“假”。真值判断是二元的,所以在C语言中,可以很简单地用“1”表示“真”,“0”表示“假”。
C++支持C语言中的这种定义,同时为了让代码更容易理解,引入了一种新的数据类型——布尔类型bool。bool类型只有两个取值:true和false,这样就可以非常明确地表示逻辑真假了。bool类型通常占用8位(1个字节)。
boolbl=true;
cout"bl="blendl;
cout"bool类型长度为:"sizeofblendl;
我们可以看到,true和false可以直接赋值给bool类型的变量,打印输出的时候,true就是1,false就是0,这跟C语言里的表示其实是一样的。
5.浮点类型
跟整数对应,浮点数用来表示小数,主要有单精度float和双精度double两种类型,double的长度不会小于float。通常,float会占用4个字节(32位),而double会占用8个字节(64位)。此外,C++还提供了一种扩展的高精度类型longdouble,一般会占12或16个字节。
除了一般的小数,在C++中,还提供了另外一种浮点数的表示法,那就是科学计数法,也叫作“E表示法”。比如:5.98E24表示5.98×;9.11e-31表示9.11×10-31。
//浮点类型
floatf=3.14;
doublepi=5.2e-3;
cout"f="fendl;
cout"pi="piendl;
这就极大地扩展了我们能表示的数的范围。一般来讲,float至少有6位有效数字,double至少有15位有效数字。所以浮点类型不仅能表示小数,还可以表示(绝对值)非常大的整数。
(float和double具体能表示的范围,可以查找float.h这个头文件)
6.字面值常量
我们在给一个变量赋值的时候,会直接写一个整数或者小数,这个数据就是显式定义的常量值,叫做“字面值常量”。每个字面值常量也需要计算机进行保存和处理,所以也都是有数据类型的。字面值的写法形式和具体值,就决定了它的类型。
(1)整型字面值
整型字面值就是我们直接写的一个整数,比如30。这是一个十进制数。而计算机底层是二进制的,所以还支持我们把一个数写成八进制和十六进制的形式。以0开头的整数表示八进制数;以0x或者0X开头的代表十六进制数。例如:
30十进制数
八进制数
0x1E十六进制数
这几个数本质上都是十进制的30,在计算机底层都是一样的。
在C++中,一个整型字面值,默认就是int类型,前提是数值在int能表示的范围内。如果超出int范围,那么就需要选择能够表示这个数的、长度最小的那个类型。
具体来说,对于十进制整型字面值,如果int不够那么选择long;还不够,就选择longlong(不考虑无符号类型);而八进制和十六进制字面值,则会优先用无符号类型unsignedint,不够的话再选择long,之后依次是unsignedlong、longlong和unsignedlonglong。
这看起来非常复杂,很容易出现莫名其妙的错误。所以一般我们在定义整型字面值时,会给它加上一个后缀,明确地告诉计算机这个字面值是什么类型。
默认什么都不加,是int类型;
l或者L,表示long类型;
ll或者LL,表示longlong类型;
u或者U,表示unsigned无符号类型;
我们一般会用大写L,避免跟数字1混淆;而u可以和L或LL组合使用。例如uLL就表示这个数是unsignedlonglong类型。
(2)浮点型字面值
前面已经提到,可以用一般的小数或者科学计数法表示的数,来给浮点类型赋值,这样的数就都是“浮点型字面值”。浮点型字面值默认的类型是double。如果我们希望明确指定类型,也可以加上相应的后缀:
f或者F,表示float类型
l或者L,表示longdouble类型
这里因为本身数值是小数或者科学计数法表示,所以L不会跟long类型混淆。
(3)字符和字符串字面值
字符就是我们所说的字母、单个数字或者符号,字面值用单引号引起来表示。字符字面值默认的类型就是char,底层存储也是整型。
而多个字符组合在一起,就构成了“字符串”。字符串字面值是一串字符,用双引号引起来表示。
‘A’字符字面值
“HelloWorld!”字符串字面值
字符串是字符的组合,所以字符串字面值的类型,本质上是char类型构成的“数组”(array)。关于数组的介绍,我们会在后面章节详细展开。
?转义字符
有一类比较特殊的字符字面值,我们是不能直接使用的。在ASCII码中我们看到,除去字母、数字外还有很多符号,其中有一些本身在C++语法中有特殊的用途,比如单引号和双引号;另外还有一些控制字符。如果我们想要使用它们,就需要进行“转义”,这就是“转义字符”。
C++中规定的转义字符有:
其中,经常用到的就是符号中的问号、双引号、单引号、反斜线,还有换行符和制表符。
//转义字符
chartchar=\n;
cout"tchar="tcharendl;
cout"HelloWorld!\t\"HelloC++!\""endl;
(4)布尔字面值
布尔字面值非常简单,只有两个:true和false。
7.类型转换
我们在使用字面值常量给变量赋值时会有一个问题,如果常量的值超出了变量类型能表示的范围,或者把一个浮点数赋值给整型变量,会发生什么?
这时程序会进行自动类型转换。也就是说,程序会自动将一个常量值,转换成变量的数据类型,然后赋值给变量。
//1.整数值赋给bool类型
boolb=25;//b值为true,打印为1
//2.bool类型赋值给算术整型
shorts=false;//s值为0
//3.浮点数赋给整数类型
inti=3.14;//i值为3
//4.整数值赋给浮点类型
floatf=10;//f值为10.0,打印为10
//5.赋值超出整型范围
unsignedshortus=;//us值为0
s=;//s值为-
转换规则可以总结如下:
非布尔类型的算术值赋给布尔类型,初始值为0则结果为false,否则结果为true;
布尔值赋给非布尔类型,初始值为false则结果为0,初始值为true则结果为1;
浮点数赋给整数类型,只保留浮点数中的整数部分,会带来精度丢失;
整数值赋给浮点类型,小数部分记为0。如果保存整数需要的空间超过了浮点类型的容量,可能会有精度丢失。
给无符号类型赋值,如果超出它表示范围,结果是初始值对无符号类型能表示的数值总数取模后的余数。
给有符号类型赋值,如果超出它表示范围,结果是未定义的(undefined)。此时,程序可能继续工作,也可能崩溃。
C++中的数据类型转换,是一个比较复杂的话题。我们这里先了解一下变量赋值时的自动类型转换,关于更加复杂的转换,我们会在下一章继续介绍。