Skip to content

Latest commit

 

History

History
684 lines (379 loc) · 32.8 KB

C++ Primer Plus 数据处理.md

File metadata and controls

684 lines (379 loc) · 32.8 KB
title date tags categories
C++ Primer Plus--数据处理(三)
2018-08-31 07:33:14 -0700
C++
C++ Primer Plus学习笔记

介绍C++变量的命名规则,以及数据类型

3.1 简单变量

int a;
a = 5;

上述语句告诉程序,它正在存储整数,并使用名称a来表示该整数的值。实际上,程序将找到一块能够存储整数的内存,将该内存单元标记为a,并将5复制到该内存单元中,然后,可以在程序中使用a来访问该内存单元。可以通过&运算符来检索a的内存地址。

变量名

C++提倡使用有一定含义的变量名,如果表示差旅费,应将其命名为cost_of_trip或costOfTrip,而不要命名为a或x等。必须遵循几种简单的C++命名规则:

  • 在名称中只能使用字母字符、数字和下划线(_);
  • 名称的第一个字符不能是数字;
  • 区分大小写
  • 不能将C++关键字用作名称
  • 以两个下划线或下划线和大写字母开头的名称被保留给实现(编译器及其使用的资源)使用,以一个下划线开头的名称被保留给实现,用作全局标识符;
  • C++对于名称的长度无限制,名称中所有的字符都有意义。

最后两点和前几点不同,因为使用_time_stop或_Donut或__time这样的名称不会导致编译器错误,而会导致不确定性。即,不知道结果将是什么,不出现编译错误的原因是,这样的名称不是非法的,但是要保留给现实使用。全局名称指的是名称被声明的位置。

命名方案

在给变量命名时,可能在变量名中加入其他信息,即描述变量类型或内容的前缀,例如,可以将整型变量命名myWeight命名为intMyWeight或nMyWeight,这样,在阅读代码时,一目了然,每个变量的意义和类型。常以这种方式使用的前缀用:str或sz(表示以空字符结束的字符串)、b(表示布尔值)、p (表示指针)、c(表示单个字符)。

整型

整型是没有小数部分的数字,比如:98,-86,0。C++提供了好几种整型来存储整数。使用的内存越大,可以表示的整数值范围也越大。另外,有的类型(符号类型)可以表示正值和负值,而有的类型(无符号类型)不能表示负值。术语宽度用户描述存储整数时使用的内存量,使用的内存越多,则越宽。C++的基本整型(按照宽度递增的顺序排序):

  • char:该类型有一些特殊属性,常用来表示字符,而不是数字
  • short
  • int
  • long
  • long long

计算机内存由一些叫做位(bit)的单元组成。C++中short、int、long和long long类型都是通过使用不同数目的位来存储值。

在不同的系统中,每种的类型的宽度都不一样,C++提供了一种灵活的标准,确保了最小长度(从C语言借鉴而来),如下:

  • short至少16位
  • int至少与short一样长
  • long至少32位,且至少与int一样长
  • long long至少64位,且至少与long一样长

实际上short是short int的简称,long是long int的简称。

位与字节

计算机内存的基本单元时位(bit),字节(byte)通常指的是8位的内存单元。从这个意义上说,字节指的就是描述计算机内存量的度量单位,1KB等于1024字节,1MB等于1024KB。

8位的内存块可以设置出256种不同的组合,因为每一位由两种设置(0和1),所以8位的总组合位:2^8-256。因此8位单元可以表示0-255或-128到127。每增加一位,组合数变加倍。

C++中对字节的定义与此不同,C++字节由至少能够容纳实现的基本字符集的相邻位组成,也就是说可能取值的数目必须等于或超过字符数目。在美国,基本字符集通过是ASCII和EBCDIC集,它们都可以用8位来容纳,所以在使用这两种字符集的系统中,C++通常包括8位。然而,国际编程可能需要使用更大的字符集,如Uunicode,因此有些实现可能使用16位甚至32位的字符。

C++中检测类型的长度,sizeof运算符返回类型或变量的长度,单位为字节。 比如:sizeof(int),或者 对一个变量利用sizeof,如:sizeof n_short (short n_short;)。

在头文件climits(旧版本limits.h)中包含了关于整型限制的信息。具体地说,它定义了表示各种显示的符合名称。比如:INT_MAX 为int的最大值,CHAR_BIT为字节的位数。 SHRT_MAX为short的最大值,LLONG_MAX为long long的最大值,LONG_MAX为long的最大值。INT_MIN为int的最小值。这里只给出了部分,详细的可以查看书籍P41。

** 符号常量--预处理方式**

climits文件中包含于下面类似的语句行:

#define INT_MAX 32767

在C++编译过程中,首先将源代码传给预处理器。在这里,#define和#include一样,也是一个预处理编译指令。该编译指令告诉预处理器:在程序中查找INT_MAX,并将所有的INT_MAX替换为32767。

初始化

初始化将复制和声明合并在一起。如下:

int n_int = INT_MAX;

可以将变量初始化为另一个变量,条件是后者是已经定义过,甚至可以使用表达式来初始化变量,条件是当程序执行到该声明时,表达式中的所有值都已经定义过:

int uncles = 5;
int aunts = uncles;
int chairs = aunts + uncles + 4;

前面的初始化继承自C语言,C++还有另一个C语言没有的初始化语法:

int wrens(432);

注意: 如果不对函数内部定义的变量进行初始化,该变量的值将是不确定的。这意味着该变量的值将是它被创建之前,相应内存单元保存的值。

在变量声明时对它进行初始化,可避免以后忘记给它赋值的情况发生。

C++11初始化方式

另一种初始化方式,这种方式用户数组和结构,但在C++98中,也可以用于单值变量:

int hamburgers = {24};

将大括号初始化器用于单值变量的情形不多,但在C++标准使得这种情形更多了。首先,采用这种方式时,可以使用等号(=),也可以不使用:

int emus{7};
int rheas = {12}

其次,大括号可以为空,即变量被初始化为零:

int rocs = {};
int psychis{};

无符号类型

前面介绍的4种整数都有一种不能存储负数值的无符号变体。其优点是可以增大变量能够存储的最大值。例如:如果short表示的范围为-32768到32767,则无符号版本的表示范围为0-65535。当然,仅当数值不会为负时才应该使用无符号类型,如人口、件数等。创建无符号版本的基本整性,只需要使用关键词unsigned来修改:

unsigned short change;
unsigned long long lang_lang;

注意:unsigned 本身是 unsigned int 的缩写。

对于一个short变量,取short的最大值(32767),对于符号整型,对最大值加一,则该值将变为-32768。对于无符号整型,对最大值加一,则该值将变为:32768。如果将无符号整数0,减一则该值会变成65535。

可以看出,这些整型变量的行为就像里程表,如果超越了限制,其值将为范围另一端的取值。

选择整型类型

int被设置为对目标计算机而言最为“自然”的长度。自然长度指的是计算机处理起来效率最高的长度。如果没有其他要求,则应该使用int。

如果知道变量可能表示的整数值大于16位整数的最大可能值,则使用32位。即使系统上int为32位,也应该这样。如果存储的值大于20亿,可使用long long。

如果short比int小,则使用short可以节省内存。通常,仅当有大型整型数组时,才有必要使用short。如果节省内存很重要,则应使用short而不是int,即使它们的长度一样。例如,如果将程序从int为16位的系统移植到int为32的系统,则用户存储int数组的内存量将加倍,但short不会受影响。

如果只需要使用一个字节,可使用char。

整型字面量

整型字面量(常量)是显式地书写常量,如212或1776。C++能够以三种不同的计数方式来书写整型:基数为10、8和16。如果第一位为19,则基数为10(十进制);如果第一位为0,第二位17,则基数为8(八进制);如果前两位为0X或0x,则基数为16(十六进制)。具体进制问题:

在默认情况下,cout以十进制格式显示整数,而不管这些整数在程序中如何书写,如:

int waits = 0X42;
cout << waits; 
//输出结果为66

这些表达方式仅仅是为了表达上的方便。例如:如果CGA视频内存段为十六进制B000,则不必在程序中使用之前将它转换为十进制45056,而只需使用0xB000。但是不管把值书写为10、012还是0xA,都将以相同的方式存储在计算机中---被存储为二进制。

如果要以十六进制或八进制方式显示值,则可以使用cout的一些特殊特性。头文件iostream提供了控制符endl,用于指示cout重起一行。同样,它还提供了控制符dec、hex和oct,分别表示用于指示cout以十进制、十六进制和八进制格式显示整数。

using namespace std;
int chest = 42;
cout << hex;
cout << chest; //输出2a
cout << oct;
cout << chest; //输出52

诸如cout<<hex;等代码不会显示在屏幕上显示任何内容,而只是修改cout显示整数的方式。因此,控制符,hex实际上是一条消息,告诉cout采取何种行为。

C++如何确定常量的类型

程序的声明中将特定的整型变量的类型告诉了C++编译器,但编译器是如何确定常量的类型呢?假设在程序中使用一个常量表示一个数字:

cout << "Year = " << 2018 << endl;

程序将1492存储为int、long还是其他类型呢?答案是,除非有理由存储为其他类型(如使用了特殊的后缀来表示特定的类型,或者值太大,不能存储为int),否则C++将整型存储为int。

关于后缀,后缀是放在常量后面的字母,用于表示类型。具体如下:

| 后缀 | 类型 | |---| | l或L | long常量 | | ul | unsigned long常量 | | ll或LL | long long常量 | | LU或UL | ungigned long常量 | | ull或Ull或uLL或ULL | unsigned long long 常量 |

对于长度,C++中不同进制,采用存储整型的规则不同:

| 进制 | 存储类型 | |---| | 十进制 | 使用后面能过存储该数的最小类型来表示: int、long 、long long | | 八进制和十六进制 | 使用后面几种类型中能过存储该数的最小类型来表示: int、unsigned int、long、unsigned long、long long 、unsigned long long |

十六进制数0x9C40(40000)将被表示为unsigned int,这是因为十六进制常用来表示内存地址,而内存地址没有符号。

char类型:字符和小整数

char类型是专门为存储字符而设计的。编程语言通过使用字母的数值编码来存储字符。因此char类型是另一种整型。它足够长,能过表示计算机系统中的所有字符---字母、数字、标点符号等等。实际上,很多系统支持的字符不操作128个,因此用一个字节就可以表示所有的符号。虽然,char最常用来处理字符,但也可以将它用做比short更小的整型。

在美国,最常用的符号集是ASCII字符集。例如,字符A的编码是65,字母M的编码为77。

char ch;
cout << "Enter a chararcter: " << endl;
cin >> ch;
cout << "Thank you for the " << ch << " character" << endl;

输入M,结果输出为 Thank you for the M character.程序打印的是M,而不是77。通过查看内存可知,77是存储在变量ch中的值。这种神奇的力量来自cin和cout,而不是char,这些工具完成了转换工作。输入时,cin将键盘输入的M转换为77;输出是cout将77转换为M。

如果将77存储在int变量中,则cout将把它显示为77(也就是说cout显示两个字符7)。C++中书写字符字面量:将字符用单引号括起来。如'M'。(注意:C++对字符用单引号对字符串使用双引号,这两者差别比较大)。cout引入一项新特性--cout.put()函数,该函数显示一个字符。

char ch = 'M'; 
int i = ch;
cout << i; //结果为77
ch = ch + 1;
i = ch;
cout << i //结果为78
cout.put(ch); //结果为N

上面程序中,ch存储的实际上是整数,因此可以对其使用整数操作,如加一。

char ch;
cin >> ch; //输入5,实际上ch存储的整数53(5的ASCII编码)

上述代码将读取字符“5”.并将其对应的字符编码存储到ch变量中。

int n;
cin >> n; //输入5

上述代码获取字符“5”,并将其转换为对应的数字5,并存储到n变量中。

成员函数cout.put()

cout.put()成员函数提供了一种显示字符的方法,可以代替<<运算符。cout.put存在的必要性:在c++的Release2.0之前,cout将字符变量显示为字符,而将字符常量(如‘M’和‘N’)显示为数字。问题是,C++早期版本将字符常量存储为int类型。也就是说,‘M’的编码将被存储在一个16或32为单元中。而char变量一般占8位。下面的语句从常量“M”中赋值左边的8位到变ch中:

char ch = 'M'

遗憾的是,对于cout来说,‘M’和ch看上去有天壤之别,虽然存储的值一样。下面的语句将打印字符M的ASCII码,而不是字符M:

cout << 'M';

但是下面打印字符M:

cout.put('M');

C++Release2.0之后,将字符常量存储位char类型,而不是int,意味着cout现在可以正确处理字符常量。

char字面值

在C++中,书写字符常量的方式有多种,对于常规字符(字母,数字和标点符号),最简单的方法是将字符用单引号括起来。这种表示法代表的是字符的数字编码。例如,ASCII系统中的对应情况如下:

  • 'A'为65,即字符A的ASCII
  • 'a'为97
  • '0'为48
  • ' '为32
  • '!'为33

这种表示法优于数值编码,更加清晰,而不需要知道编码方。如果系统使用的是EBCDIC,则A的编码值不是65,但’A‘表示的仍然是字符A。

转义字符: 有些字符不能之间通过键盘输入到程序中,比如:回车等。C++提供了一种特殊的表示方法--转义字符。例如:"将双引号作为常规字符,\t表示水平制表符等等。

将转移字符作为字符常量时,应用单引号括起来;将他放在字符串中不要使用单引号。

基于字符八进制和十六进制编码来使用转义字符。例如,Ctrl+Z的ASCII码为26,对应的八进制编码为032,十六进制编码为0X1a。可以使用下面的转义字符来表示该字符: \032或\x1a。

通用字符名

通用字符名的用法类似于转义序列。通用字符名可以以\u或\U开头。\u后面是8个十六进制位,\U后面则是16个八进制位。这些位表示的是字符的ISO 10646码点。(ISO 10646是一种正在制定的国际标准,为大量的字符提供了数字编码)。

#include <iostream>
using namespace std;

int main()
{
	cout << "Let them eat g\u00E2teau.\n";
    return 0;
}

输出为:Let them eat gâteau.

Unicode提供了一种表示各种字符集的解决方案---为大量字符和符号提供标准数值编码,并根据类型给他们分类。例如:ASCII码为Unicode的子集,因此在这两种系统中,美国的拉丁字母(如A和Z)表示相同。然后,Unicode还包含其他拉丁字符。

Unicode给每个字符制定一个编号--码点。Unicode码点通常类似于:U-222B。其中U表示这是一个Unicode字符,而222B是该字符的十六进制编码。

国际标准组织(ISO)建立了一个工作组,专门开发ISO 10646---这也是对一个多语言文本进行编码的标准。ISO 10646小组和Unicode小组从1991年开始合租,以确保他们的同步。

signed 和 unsigned char

与int不同的是,char默认情况下既不是没有符号,也不是有符号。是否有符号由C++决定。

char fodo //可能为有符号,也可能无符号
unsigned char bar //无符号
signed char snark //有符号

如果char作为数值类型,unsigned char类型表示的范围为0255,而signed char表示的范围为-128127。如果使用char变量存储200这样大的值,在某些系统上可以,而在另一些系统上可能不可以。但是使用unsigned char可以在任何系统上达到这种目的。如果使用char存储ASCII字符,则有没有字符都没关系。

wchar_t

wchar_t(宽字符类型)可以表示扩展字符集。wchar_t类型是一种整数类型,它有足够的空间,可以表示系统使用的最大扩展字符集。这种类型与另一种整型(底层(underlying)类型)的长度和字符属性相同,对底层系统的选择取决于实现,因此在一个系统中,他可能是unsiged short,而在另一个系统中,则可能是int。

cin和cout将输入和输出看作是char流,因此不适于用来处理wchar_t类型。iostream头文件的最新版提供了类似的工具---wcin和wcout,可用于处理wchar_t。另外,可以通过加上前缀L来表示宽字符常量和宽字符串。

wchar_t bob = L'P';
wcout << L"tall" << bob << endl;

本书不使用宽字符类型,但应知道这种类型,尤其是在进行国际编程或使用Unicode或ISO 10646时。

C++11新增类型 char16_t和char32_t

随着编程人员的日益熟悉Unicode,类型wchar_t显然不再满足需求。事实上,在计算机系统上惊喜字符和字符串编码时,仅使用Unicode码点并不够。具体说,进行字符串编码时,如果由特定长度和符号特征的类型,将很有帮助,而类型wchar_t的长度和符号特征随实现而已。因此,C++11新增了char16_t和char32_t,两者都无符号,前者长为16为,后者长为32为。使用前缀u表示char16_t字符常量和字符串常量,如u'be goog';使用前缀U表示char32_t常量,如:U'R'。类型char16_t与/u00F6形式的通用字符名匹配,而类型char32_t与/U0000222B形式的通用字符名匹配。

char16_t ch1 = u'q';
char32_t ch2 = U'\U0000222B';

bool类型

在计算中,布尔变量的值可以时true或false。

bool is_ready = true;

字面值true和false都可以通过提升转换为int类型,true被转换为1,false被转换为0。任何非零值可以转换为true,而零被转换为false。

bool start = -100;
bool end = 0;

3.2 const限定符

使用关键字const来修改变量声明和初始化,例如,假设需要一个表示一年中月份数的符号常量:

const int Months =12;

这样,便可以在程序中使用Months,而不是12了。常量(如Months)被初始化后,其值就被固定了,编译器将不允许在修改该常量的值。如果这样做,g++将指出程序视图给一个只读变量赋值。const被叫做限定符,因为它限定了声明的含义。

一种常见的做法是将名称的首字母大写,以提醒Months是一个常量。

const比#define好,首先,它能明确指定类型;其次,可以使用C++的作用域规则将定义限制在特定的函数或文件中;最后,const用户更复杂的类型,后面介绍。

建议使用const而不是#define。

3.3 浮点数

对于浮点数,计算机将其分为两部分存储。一部分表示值,另一部分用于对值进行放大或缩小。比如:对于数字34.1245和34124.5,它们处理小数点位置不同外,其他都相同。可以把第一个数表示为0.341245(基准值)和100(缩放因子),而将第二个表表示为0.341245(相同基准值)和100(缩放因子更大)。缩放因子的作用是移动小数点的位置,术语浮点因此而得名。C++内部表示浮点数的方法与此相同,只不过是二进制,因此缩放因子是2的幂,不是10的幂。

3.3.1 书写浮点数

C++有两种书写浮点数的方式:

  • 第一种使用常用的标准小数点表示法:12.34
  • 第二种E表示法: 3.45E6指的是3.45与1000000相乘的结果;8.33E-4, 7E5,-8.33E4。

E表示法适合表示非常大和非常小的数。E表示法确保数字以浮点数格式存储,即使没有小数点。注意,既可以使用E也可以使用e,指数可以是正数也可使负数。数字中不能有空格,比如:7.2 E6是非法的。

指数为负数意味着除以10的乘方,而不是乘以10的乘方。因此8.33E-4表示$8.33/10^4$,即0.000833。数值前面的符号用于数值,而指数的符号用户缩放。

3.3.2 浮点类型

C++三种浮点类型:float、double和long double.这些类型是按照它们可以表示的有效位数和允许的指数最小范围来描述的。有效位数是数字中有意义的位。例如:14179英尺,使用了5个有效位;13000英尺,使用了2个有效位,其他3位为占位符;1.234,使用了4个有效位。

C++对有效位数的要求是,float至少32位;double至少48位,且不少于float;long double至少跟double一样多。通常,float位32位,double为64位,long double为80、96或128位。另外三种类型的指数范围至少是-37至37。

#include <iostream>
using namespace std;

int main()
{
        cout.setf(ios_base::fixed, ios_base::floatfield);
        float tub = 10.0 / 3.0;
        const float million = 1.0e6;
        double mint = 10.0 / 3.0;
        cout << "tub = " << tub <<endl;
        cout << "tubs = " << tub * million << endl;
        cout << "mint = " << mint << endl;
        cout << "mints = " << mint * million << endl;
        return 0;
}

结果: tub = 3.333333 tubs = 3333333.250000 mint = 3.333333 mints = 3333333.333333

通常cout会删除结尾的零,例如,将3333333.250000显示为 3333333.25。调用cousetf()将覆盖这种行为。tub和mint都被初始化为10.0/3.0=3.333333333333....,由于cout打印6位小数,因此tub和mint都是精确的。但是当程序将每个数乘以一百万后,tub在第7个3之后就不正确的值有了误差。然后,double类型的变量显示了13个3,因此它至少有13位精确。这是由于float的精度限制。

读取包含文件:程序中的所有文件都存在于计算机中,或位于计算机可以使用的地方。找到使用的包含文件,看看它们包含的内容,很快发现,源文件和头文件都是知识和信息的很好来源。

浮点常量

与整型相比,浮点数优点:

  • 可以表示整数之间的值
  • 由于有缩放因子,可以表示的范围大得多

浮点数缺点:

  • 浮点运算的速度通常比整数运算慢
  • 精度将降低

fltadd.cpp程序:

#include <iostream>
using namespace std;

int main()
{
        float a = 2.34e22;
        float b = a +  1.0;

        cout << "a = " << a << endl;
        cout << "a - b = " << b -a << endl;
        return 0;
}

结果: a = 2.34e+22 a - b = 0

问题在于,2.34e22是一个小数点左边有23位的数字。加上1,就是在第23位加1。但是float类型只能表示数字中的前6或前7位,因此修改第23位对这个值不会有影响。

3.4 C++算术运算符

C++中常用的运算符:

  • +运算对操作数执行加法
  • -运算
  • *运算
  • /运算
  • %运算符求模,生成第一个数除以第二个数后的余数。例如19%6=1。如果其中一个是负数,则结果的符号满足如下规则:(a/b)*b+a%b = a,%的操作数必须是整数。

arith.cpp

#include <iostream>
using namespace std;

int main()
{
        float a = 10.23;
        float b = 11.22;
        cout.setf(ios_base::fixed, ios_base::floatfield);

        cout << "a = "<<  a <<endl;
        cout << "a + b = " <<  a + b << endl;
        return 0;
}

结果:a = 10.230000 a + b = 21.450001

这是由于float保证6位有效位。

3.4.1 运算符优先级和结合性

先乘除,后加减

*、/和%的优先级相同

float logs = 120 / 4* 5;

对于上式,运算符/和$$的优先级相同,因此优先级本身不能指出程序究竟是先计算120除以4,还是先计算45。两种结果截然不同。

当两个运算符优先级相同时,C++将看操作数的结合性是从左到右还是从右到左。从左到右意味着如果两个优先级相同的运算符被同时用于一个操作数,则首先应用左侧的运算符。查表得,乘除是从左到右结合得,所以上述式子应先计算120/4,得到结果30再除以5。

3.4.2 除法分支

除法运算符(/)的行为取决于操作数的类型。如果两个操作数都是整数,则C++将执行整数除法,这意味着小数部分将被丢弃,使得最后的结果是一个整数。如果其中有一个(或两个)操作数是浮点数,则小数部分保留,结果为浮点数。

除法运算符表示了3种不同的运算:int除法、float除法、double除法。C++根据上下文来确定运算符的含义,使用相同的符号进行多种操作叫做运算符重载。

3.4.3 求模运算符

求模运算符返回整数除法的余数,它与整数除法先结合,尤其适用于解决要求将一个量分成不同的整数单元的问题,例如:英寸转换为英尺和英寸。

将181镑,转换为英石和镑。一英石等于14磅.

#include <iostream>
using namespace std;

int main()
{
        const int lbs_per_stn = 14;

        int lbs = 181;
        int stone = lbs / lbs_per_stn;
        int pounds = lbs % lbs_per_stn;
        cout << lbs << " pounds are " << stone << " stone, "
        <<  pounds << " pound(s)."<< endl;
        return 0;
}

结果:181 pounds are 12 stone, 13 pound(s).

3.4.4 类型转换

C++自动执行类型转换:

  • 将一个算术类型的值赋给另一种算术类型的变量
  • 表达式种包含不同类型
  • 将参数传递给函数

初始化和赋值进行的转换

C++允许将一种类型的值赋给另一种类型的变量。值将被转换为接受变量的类型。假设so_long的类型为long,thirty的类型为short,而程序中有:

so_long = short

则进行赋值时,程序将thirty的值(short通常为16位)扩展为long值(通常32位)。扩展后将得到一个新值,这个值存储再so_long中,而thirty的内容不变。

将一个值赋给取值范围更大的类型通常不会导致问题。例如,将short值赋给long变量并不会改变值,只是占位符的字节更多而已。

然而,建一个很大的long(如2111222333)值赋给float的变量将降低精度。因为float只有6位有效数字,因此这个值将被四舍五入为2.11122E9。因此,有些转换是不安全的。

潜在的数值转换问题:

  • 将较大的整数转换为较小的整型,如将long转换为short,原来的值可能超出目标类型的取值范围,通常只复制右边的字节;
  • 将浮点数转换为整型,小数部分丢失(不是四舍五入),原来的值可能超出目标类型的取值范围,在这种情况下,结果可能不确定;
  • 将较大的浮点类型转换位较小的浮点类型,如将double转换为float,精度(有效位位数)降低,,原来的值可能超出目标类型的取值范围,在这种情况下,结果可能不确定;

将0赋值给bool变量,将被转换为false;而非零将被转换为true。

以{}方式初始化时进行的转化(C++11)

C++将使用大括号的初始化称为列表初始化,因为这种初始化常用于给复杂的数据类型提供值列表。对类型的转化要求更严格。具体,列表初始化不允许缩窄,即变量的类型可能无法表示赋给它的值。例如,不允许浮点数转换为整型。在不同的整型之间转换或将整型转换为浮点型可能被允许。例如:可见long变量初始化为int类型的值,因为long总是至少与int一样长;相反转换也可能被允许,只要int变量能够存储给它的long常量。

表达式中的转换

在计算表达式时,C++将bool、char、unsigned char、signed char和short值转换为int。具体地说,true被转换为1,false被转换为0,这些转换称为整型提升。

整型提升还包括:如果short比int短,则unsigned short类型转换为int;如果两种类型的长度相同,则unsigned short类型转换为unsigned int。这种规则保证对unsigned short提升时不会损失数据。

将不同类型进行算术运算时,也会进行一些转换。比如将int和float相加。当运算涉及两种类型时,较小的类型被转换为较大的类型。编译器通过校验表来确定算术表达式中执行的转换,校验表如下:

  • 如果有一个操作数的类型为long double,则将另一个操作数转换为long double;
  • 否则,如果有一个操作数的类型为double,则将另一个操作数转换为double;
  • 否则,如果有一个操作数的类型为float,则将另一个操作数转换为float;
  • 否则,说明操作数都是整型,因此执行整型提升;
  • 在这种情况下,如果两个操作数都是有符号或无符号的,且其中一个操作数的级别比另一个低,则转换为高级别的类型;
  • 如果一个操作数有符号,另一个操作数无符号,且无符号操作数的级别比有符号操作数的级别高,则将有符号操作数转换为无符号的操作数类型;
  • 否则,如果有符号类型可表示无符号类型的所以可能值,则将无符号类型转换为有符号操作数所属的类型;
  • 否则,将两个操作数都转换为无符号版本。

参数传递的转换

传递参数时的类型转换通常由C++函数原型控制。

强制类型转换

C++允许通过强制转换机制显示地进行类型转换。强制类型转换格式有两种,例如:为将存储在变量thorn中的int值转换为long类型,如下:

(long)thorn//来自C
long(thorn)//C++新格式:像函数调用一样

强制转换不会改变thorn本身,而是创建一个新的、指定类型的值。

C++还引入了4个强制类型转换符,对他们的使用要求更严格。后面介绍,在这四种运算符中,static_cast<>可用于将一种数值转换为另一个种数值类型,例如将thorn转换为long类型:

static_cast<long> (thorn)

推广之,可以这样做:

static_cast<typename> (value)

typecast.cpp

#include <iostream>
using namespace std;

int main()
{
        int auks, bats, coots;
        auks = 11.99 + 19.99; //相加赋值给int变量,被截断为31,

        bats = int(11.99) + int(19.99);//先截断,在相加
        coots = (int) 11.99 + (int) 19.99;
        cout << "auks = " << auks << ", bats = " << "bats"
        << ", coots = " << coots<< endl;

        char ch = 'Z';
        cout << "The code for " << ch <<  " is "
        << int(ch) << endl;
        cout << "Yes, the code is " << static_cast<int>(ch) << endl;
        return 0;
}

auks = 31, bats = bats, coots = 30 The code for Z is 90 Yes, the code is 90

3.4.5 C++11中的auto声明

C++新增了一个工具,让编译器能够根据初始值的类型推断变量的类型。为此,它重新定义了auto的含义。auto是C语言的关键子,当很少使用。在初始化声明中,如果使用关键字auto,而不指定变量类型,编译器将把变量的类型设置成与初始值相同:

auto n = 100; //n是整型
auto x = 1.5 // x is double
auto y = 1.3e12L // y is long double 

然而,自动推断类型并非为这种简单情况而设计;事实上,如果将其用于这种简单情形,甚至可能让您误入歧途。例如,假设要将x、y、z都指定为double,并编写了如下代码:

auto x = 0.0; //浮点型
double y = 0; //浮点型
auto z = 0; //整型

处理复杂类型,如标准模块库(STL)中的类型,自动类型推断的优势才能显现出来。例如,对于下述C++98代码:

std:vector<double> scores;
std:vector<double>::iterator pv = scores.begin();

C++允许您将其重写为:

std::vector<doble> scores;
auto pv = scores.begin()

3.5 总结

C++的基本类型分为两组:

  • 由存储为整型的值组成
  • 由存储为浮点格式的值组成

整型通过存储值时的内存量及有无符号来区分。整型从最小到最大依次是:

bool \ char \ signed char \ unsigned char \ short \ unsigned short \ int \ unsigned int \ long \ unsigned long \ long long \ unsigned long lon

3种浮点类型:

float \ double \ long double