专栏简介:本专栏主要面向C++初学者,解释C++的一些基本概念和基础语言特性,涉及C++标准库的用法,面向对象特性,泛型特性高级用法。通过使用标准库中定义的抽象设施,使你更加适应高级程序设计技术。希望对读者有帮助!
目录
- 2.1基本内置类型
- 算术类型
- 带符号类型和无符号类型
- 类型转换
- 含有无符号类型的表达式
- 字面值常量
- 字符和字符串字面值
- 转义序列
- 指定字面值的类型
- 布尔字面值和指针字面值
2.1基本内置类型
C++定义了一套包括算术类型(arithmetic type〉和空类型(void)在内的基本数据类型。其中算术类型包含了字符、整型数、布尔值和浮点数。空类型不对应具体的值,仅用于一些特殊的场合,例如最常见的是,当函数不返回任何值时使用空类型作为返回类型。
算术类型
算术类型分为两类:整型(integral type,包括字符和布尔类型在内)和浮点型。算术类型的尺寸(也就是该类型数据所占的比特数)在不同机器上有所差别。表2.1列出了C++标准规定的尺寸的最小值,同时允许编详器赋予这些类型更大的尺寸。某一类型所占的比特数不同,它所能表示的数据范围也不一样。
表2.1: C++:算术类型
类型 | 含义 | 最小尺寸 |
---|---|---|
boo1 | 布尔类型 | 未定义 |
char | 字符 | 8位 |
wchar_t | 宽宇符 | 16位 |
char16_t | Unicode字符 | 16位 |
char32_t | Unicode字符 | 32位 |
short | 短整型 | 16位 |
int | 整型 | 16位 |
long | 长整型 | 32位 |
long long | 长整型 | 64位 |
float | 单精度浮点 | 6位有效数字 |
double | 双精度浮点 | 10位有效数字 |
long double | 扩展精度浮点数 | 10位有效数字 |
布尔类型(bool)的取值是真(true)或者假(false)。
C++提供了几种字符类型,其中多数支持国际化。基本的字符类型是char,一个char的空间应确保可以存放机器基本字符集中任意字符对应的数字值。也就是说,一个char的大小和一个机器字节一样。
其他字符类型用于扩展字符集,如wchar_t、char16_t、char32_Et。wchart类型用于确保可以存放机器最大扩展字符集中的任意一个字符,类型char16_t和
char32_t则为Unicode字符集服务(Unicode是用于表示所有自然语言中字符的标准)。
除字符和布尔类型之外,其他整型用于表示(可能不同尺寸的整数。C++语言规定-个int至少和一个short一样大,一个long至少和一个int一样大,一long 1ong
至少和一个long一样大。其中,数据类型longlong是在C++II中新定义的。
浮点型可表示单精度、双精度和扩展精度值。C++标准指定了一个浮点数有效位数的最小值,然而大多数编译器都实现了更高的精度。通常,float以1个字(32比特来表示,double以2个字(64比特来表示,long double以3或4个字(96或128比特)来表示。一般来说,类型float和double分别有7和16个有效位;类型long
double则常常被用于有特殊浮点需求的硬件,它的具体实现不同,精度也各不相同。
带符号类型和无符号类型
除去布尔型和扩展的字符型之外,其他整型可以划分为带符号的(signed)和无符号的(unsigned)两种。带符号类型可以表示正数、负数或0,无符号类型则仅能表示大于等于0的值。
类型int、short、long和long long都是带符号的,通过在这些类型名前添加unsigned就可以得到无符号类型,例如unsignedLong。类型unsignedint可以
缩写为unsigned。
与其他整型不同,字符型被分为了三种:char、signed char和unsigned char。
特别需要注意的是:类型char和类型signed char并不一样。尽管字符型有三种,但是字符的表现形式却只有两种,带符号的和无符号的。类型char实际上会表现为上述两种形式中的一种,具体是哪种由编译器决定。
无符号类型中所有比特都用来存储值,例如,8比特的unsignedchaz可以表示0至255区间内的值。
C++标准并没有规定带符号类型应如何表示,但是约定了在表示范围内正值和负值的量应该平衡。因此,8比特的signedchar理论上应该可以表示-127至127区间内的值,大多数现代计算机将实际的表示范围定为-128至127。
类型转换
对象的类型定义类对象能包含的数据和能参与的运算,其中一种运算被大多数类型支持,就是将对象从一种给定的类型转换(convert)为另一种类型。
当在程序的某处我们使用了一种类型而其实对象应该取另一种类型时,程序会自动进行类型转换。
当我们像下面这样把一种算术类型的值赋给另外一种类型时:
bool b = 42;// b为真
int i= b;//i的值为1
i = 3.14;
double pi = i;// pi的值为3.0
unsigned char c = -1; // 假设char 占 8 比特,c的值为 255
signed char c2 = 256; // 假设char 占 8比特,c2的值是未定义的
类型所能表示的值的范围决定了转换的过程:
- 当我们把一个非布尔类型的算术值赋给布尔类型时,初始值为0则结果为false,否则结果为true。
- 当我们把一个布尔值赋给非布尔类型时,初始值为false则结果为0,初始值为true则结果为1。
- 当我们把一个浮点数赋给敲数类型时,进行了近似处理。结果值将仅保留浮点数中小数点之前的部分。
- 当我们把一个整数值赋给浮点类型时,小数部分记为0。如果该整数所占的空间超过了浮点类型的容量,精度可能有损失。
- 当我们赋给无符号类型一个超出它表示范围的值时,结果是初始值对无符号类型表示数值总数取模后的余数。例如,8比特大小的unsignedchaz可以表示0至
255区间内的值,如果我们赋了一个区间以外的值,则实际的结果是该值对256取模后所得的余数。因此,把-1赋给8比特大小的hnsignedchar所得的结果
是255。 - 当我们赋给带符号类型一个超出它表示范围的值时,结果是未定义的(undefined)。此时,程序可能继续工作、可能崩溃,也可能生成垃圾数据。
当在程序的某处使用了一种算术类型的值而其实所需的是另一种类型的值时,编译器同样会执行上述的类型转换。例如,如果我们使用了一个非布尔值作为条件,那么它会被自动地转换成布尔值,这一做法和把非布尔值赋给布尔变量时的操作完全一样:
int =42;
if(i)//i5条件的值将为true
i = 0;
如果i的值为0,则条件的值为false;i的所有其他取值(非0)都将使条件为true。
以此类推,如果我们把一个布尔值用在算术表达式里,则它的取值非0即1,所以一般不宜在算术表达式里使用布尔值。
含有无符号类型的表达式
尽管我们不会故意给无符号对象赋一个负值,却可能(特别容易)写出这么做的代码。例如,当一个算术表达式中既有无符号数又有int值时,那个int值就会转换成无符号数。把int转换成无符号数的过程和把int直接赋给无符号变量一样:
unsigned u=10;
int i= - 42;
std::cout<<i+i<<std::endl;//输出-84
std::cout<<u十i<<std::endl;//如果int占32位,输出4294967264
在第一个输出表达式里,两个(负)整数相加并得到了期望的结果。在第二个输出表达式里,相加前首先把整数-42转换成无符号数。把负数转换成无符号数类似于直接给无符号数赋一个负值,结果等于这个负数加上无符号数的模。
当从无符号数中减去一个值时,不管这个值是不是无符号数,我们都必须确保结果不能是一个负值:
unsigned u1=42,u2=10;
std::cout<<u1-u2<<estd::endl;//正确:输出32
std::cout<<u2-u1<<std::endl;//正确:不过,结果是取模后的值
无符号数不会小于0这一事实同样关系到循环的写法。
//错误:变量u永远也不会小于0,徨环条件一直成立
for(unsigned u = 10; u>=0;--u)
std::cout<<u<<std::endl;
来看看当u等于0时发生了什么,这次迭代输出0,然后继续执行for语句里的表达式。表达式一u从u当中减去1,得到的结果-1并不满足无符号数的要求,此时像所有表示范围之外的其他数字一样,-1被自动地转换成一个合法的无符号数。假设int类型占32位,则当u等于0时,–u的结果将会是4294967295。
一种解决的办法是,用while语句来代典for语句,因为前者让我们能够在输出变量之前(而非之后)先减去1:
unsignedu=11;//确定要输出的最大数,从比它大1的数开始
while(u>0)
--u;//先减1,这样最后一次选代时就会输出0
std::cout<<u<<std::endl;
}
改写后的循环先执行对循环控制变量减1的操作,这样最后一次迭代时,进入循环的u值为1。此时将其减1,则这次迭代输出的数就是0:下一次再检验循环条件时,u的值等于0而无法再迹入循环。因为我们要先做减1的操作,所以初始化u的值应该比要输出的最大值大1。这里,u初始化为11,输出的最大数是10。
字面值常量
-个形如42的值被称作字面值常量(literal),这样的值一望而知。每个字面值常量都对应一种数据类型,字面值常量的形式和值决定了它的数据类型。整型和浮点型字面值我们可以将整型字面值写作十进制数、八进制数或十六进制数的形式。以0开头的整数代表八进制数,以0x或0X开头的代表十六进制数。例如,我们能用下面的任意一种形式来表示数值20:
20/*十进制*/024/*八进制+/0x14/*十六进制*/
整型字面值具体的数据类型由它的值和符号决定。默认情况下,十进制字面值是带符号数,八进制和十六进制字面值既可能是带符号的也可能是无符号的。十进制宇面值的类型是int、1long和longLong中尺寸最小的那个(例如,三者当中最小是int,当然前提是这种类型要能容纳下当前的值。八进制和十六进制字面值的类型是能容纳其数值的int、unsigned int、long、unsigned long、long long和unsigned long long中的尺寸最小者。如果一个字面值连与之关联的最大的数据类型都放不下,将产生错误。类型short没有对应的字面值。在表2.2(第37页中,我们将以后缀代表相应的字面值类型。
尽管整型字面值可以存储在带符号数据类型中,但严格来说,十进制字面值不会是负数。如果我们使用了一个形如-42的负十进制字面值,那个负号并不在字面值之内,它的作用仅仁是对字面值取负值而已。浮点型字面值表现为一个小数或以科学计数法表示的指数,其中指数部分用E或e标识:
3.14159 3.14159E0 0.0 e0 .001
默认的,浮点型字面值是一个double,我们可以使用表2.2中的后缀来表示其他浮点型。
字符和字符串字面值
由单引号括起来的一个字符称为chaz型字面值,双引号括起来的零个或多个字符则构成字符串型字面值。
'a' //字符字面值
“HelloWorld!““//字符定字面值
字符串字面值的类型实际上是由常量字符构成的数组(array)。编译器在每个字符串的结尾处添加一个空字符(“\0“),因此,字符略字面值的实际长度要比它的内容多1。例如,字面值「A“表示的就是单独的字符&,而字符串“A“则代表了一个字符的数组,该数组包含两个字符:一个是字母A、另一个是空字符。
如果两个字符串字面值位置紧邻且仅由空格、缩进和换行符分隔,则它们实际上是一个整体。当书写的字符串字面值比较长,写在一行里不太合适时,就可以采取分开书写的方式:
//分多行书写的字符定字面值
std::cout<<“areally,Feallylongstring1iteral“
“thatSpansEwo1ines“<<std::endl}
转义序列
有两类字符程序员不能直接使用:一类是不可打印(Cnonprintable)的字符,如退格或其他控制字符,因为它们没有可视的图符;另一类是在C++语言中有特殊含义的字符(单引号、双引号、问号、反斜线)。在这些情况下需要用到转义序列(escapesequence),转义序列均以反斜线作为开始,C++语言规定的转义序列包括:
描述 | 符号 | 描述 | 符号 | 描述 | 符号 |
---|---|---|---|---|---|
换行符 | \a | 横向制表符 | \t | 报警(响铃)符 | \a |
纵向制表符 | \v | 退格符 | \b | 双引号 | " |
反斜线 | \\ | 问号 | ? | 单引号 | ’ |
回车符 | \r | 进纸符 \f |
在程序中,上述转义序列被当作一个字征使用:
std::cout << "\n";//转到新一行
std::cout << "\tHi!\n"; //输出一个制表符,输出“Hi!“,转到新一行
我们也可以使用泛化的转义序列,其形式是\x后紧跟1个或多个十六进制数字,或者\后紧跟1个、2个或3个八进制数字,其中数字部分表示的是字符对应的数值。假设使用的是Latin-1字符集,以下是一些示例:
\7(响铃) \12(换行符) \40(空格)
\0(空字符) \115(字符M) \x4d(字符M)
我们可以像使用普通字符那样使用C++语言定义的转义序列:
std::cout<<“Hi \t x4dO\115!\n" ;//输出Hi MOM! ,转到新一行
std::cout<<‘\115’<<“\n"; //输出M,转到新一行
注意,如果反斜线\后面跟着的八进制数字超过3个,只有前3个数字与\构成转义序列。例如,“\x1234“表示2个字符,即八进制数123对应的字符以及字符4。相反,\x要用到后面跟着的所有数字,例如,“\x1234“表示一个16位的字符,该字符由这4个十六
进制数所对应的比特唯一确定。因为大多数机器的chaz型数据占8位,所以上面这个例子可能会报错。一般来说,超过8位的十六进制字符都是与表2.2中某个前缀作为开头的扩展字符集一起使用的。
指定字面值的类型
通过添加如表2.2中所列的前缀和后缀,可以改变整型、浮点型和字符型字面值的默认类型。
L'a' // 宽字符型字面值,类型是wchar上
u8"hi!" //utf-8字符定字面值(utf-8用8位编码一个Unicode字符)
42ULL //无符号整型字面值,类型是unsigned long long
1E-3F //单精度浮点型字面值,类型是float
3.14159D//扩展精度浮点型字面值,类型是long double
表2.2:指定字面值的类型
字符串和字符字面值
前缓 | 含义 | 类型 |
---|---|---|
u | Unicode16宇律 | char16_t |
U | Unicode32字符 | char32_t |
L | 宽字符 | wchar_t |
u8 | UTF-8(仅用于字符串字面常量) | char |
整型字面值浮点型字面值
后缔 | 最小匹配类型 | 后级 | 类型 |
---|---|---|---|
u or U | unsigned | f或F | float |
l or L | long | l 或 L | long double |
ll or LL | long long |
对于一个整型字面值来说,我们能分别指定它是否带符号以及占用多少空间。如果后缀中有U,则该宇面值属于无符号类型,也就是说,以U为后缀的十进制数、八进制数或十六进制数都将从unsigned int、unsigned long和unsigned long long中选择能匹配的空间最小的一个作为其数据类型,如果后缀中有L,则字面值的类型至少是long;如果后缀中有LL,则字面值的类型将是longlong和unsigned long long中的一种。显然我们可以将U与或LL合在一起使用。例如,以UL为后缀的字面值的数据类型将根据具体数值情况或者取unsigned 1ong,或者取unsigned long long。
布尔字面值和指针字面值
true和false是布尔类型的字面值:
bool test=false;
nullptr是指针字面值。