C++ Primer 第2章 变量和基本类型
2.1 基本内置类型
算数类型
bool的最小尺寸未定义
char16_t和char32_t的最小 尺寸分别为16位和32位,含义是Unicode字符,short和int都是16位(-32768~32768),long是32位(-21E~21E),long long是64位。
float是6位有效数字,double是10位有效数字,long double是10位有效数字。
以下是选择类型的一些经验准则:
- 当明确知晓数值不可能位负时,选用无符号类型。
使用int执行整数运算。在实际应用中,short常常显得太小而long一般和int有一样的尺寸。如果数值超过int的表示范围,选用long long。
在算术表达式中不要使用char或bool,因为类型char在一些机器上是有符号的,一些是没符号的。如果需要使用一个不大的整数,那么明确指定是signed char(-128~127)或者unsigned char(0~255)
- 执行浮点数运算选用double,这是因为float通常精度不够,而且double和float的计算代价相差无几。对于某些机器来说,甚至double比float更快。long double在一般情况下是没有必要的,况且它带来运行时的消耗也不容忽视。
类型转换
- 当赋值给无符号类型一个超出它表示范围的值时,结果是初始值对无符号类型表示数值总数取模后的余数。例如复制给8比特大小的unsigned char可表示0~255区间内的值,如果赋值了一个区间意外的值,则实际结果是对该值对256取模后所得的余数。因此,把-1赋给8比特大小的unsigned char 所得结果是255。
- 当我们赋值给带符号类型一个超出它表示范围的值时,结果是未定义的(undefined)。
- 当一个算术表达式中既有无符号数又有int值时,那个int值就会转换成无符号数。(先转换再计算)
字面值常量
十进制字面值的类型是int、long和long long中尺寸最小的那个。
八进制和十六进制字面值的类型是int、unsigned int、long、unsigned long、long long和unsigned long long中的尺寸最小者。
如果一个字面值连与之关联的最大的数据类型都放不下,将产生错误。类型short没有对应的字面值。
尽管整型字面值可以存储在带符号数据类型中,但严格来说,十进制字面值不会是负数,负号并不在字面值之内,它的作用仅仅是对字面值取负数而已。
由单引号括起来的一个字符称为char型字面值,双引号括起来的零个或多个字符则构成字符串型字面值,编译器会在每个字符串的结尾处添加一个空字符’\0’,因此,字符串字面值的实际长度比内容多1。
泛化的转义序列:\x后紧跟1个或多个十六进制数字,或者\后紧跟1个、2个或3个八进制数字,其中数字部分表示的是字符对应的数值。
注意:如果反斜线\后面跟着的八进制数字超过3个,只有前三个数字与\构成转义序列。相反,\x要用到后面跟着的所有数字。
2.2 变量
变量定义
WARNING:初始化不是赋值,初始化的含义是创建变量时赋予其一个初始值,而赋值的含义是把对象的当前值擦除,而以一个新值来替代。
C++11:可用{ }来初始化变量
如果想声明一个变量而非定义它,就在变量名前添加关键字extern,而且不要显示的初始化变量。任何包含了显示初始化的声明即称为定义。会抵消extern的作用。
extern int i; //声明i而非定义i
int j; //声明并定义j
变量能且只能被定义一次,但是可以被申请多次。
2.3 复合类型
引用&d(左引用)
引用并非对象,它只是为一个已经存在的对象所起的另外一个名字。引用只能绑定在对象上,而不能与字面值或某个表达式的计算结果绑定在一起。
指针*d
- 指针本身就是一个对象,允许对指针赋值和拷贝,而且在指针的生命周期内它可以先后指向几个不同的对象。
- 指针无需在定义时赋值。
int ival = 42;
int *p = &ival; //p存放变量ival的地址,或者说p是指向变量ival的指针。
因为引用不是对象,没有实际地址,所以不能定义指向引用的指针。
如果指针指向了一个对象,则允许使用解引用符*来访问对象。
C++11:得到空指针最直接的办法就是用字面值nullptr来初始化指针,它可以被转换成任意其他的指针类型。
void* 指针是一种特殊的指针类型,可用于存放任意对象的地址。不能直接操作void*指针所指的对象,因为我们并不知道这个对象到底是什么类型,也就无法确定能在这个对象上做哪些操作。
2.4 const限定符
如果想在多个文件之间共享const对象,必须在变量的定义之前添加extern关键字。
常量引用是对const的引用,引用的对象是常量还是非常量可以决定其能所参与的操作,却无论如何都不会影响到引用和对象的绑定关系本身。
常量引用仅对引用可参与的操作做出了限定,对于引用的对象本身是不是一个常量未作限定。因为对象也可能是个非常量,所以允许通过其他途径改变它的值:
int i = 42;
int &r1 = i; //引用ri绑定对象i
const int &r2 = i; //r2也绑定对象i,但是不允许通过r2修改i的值
r1 = 0; //r1并非常量,i的值修改为0
r2 = 0; //错误:r2是一个常量引用
指针和const
指向常量的指针不能用于改变其所指对象的值,要想存放常量对象的地址,只能使用指向常量的指针:
const double pi = 3.14;
const double *cptr = π
两个例外:
- 允许令一个指向常量的指针指向一个非常量对象:
double dval = 3.14;
cptr = &dval; //正确:但是不能通过cptr改变dval的值
所谓指向常量的指针仅仅要求不能通过该指针改变对象的值,而没有规定那个对象的值不能通过其他途径改变。
指针本身是一个常量并不意味着不能通过指针修改其所指对象的值,能否这样做完全依赖于所指对象的类型。
把*放在const关键字之前用以说明指针是一个常量,即不变的是指针本身的值而非指向的那个值。
想让指针指向常量,就必须在前面加const,想让指针是常量,就得把*放到const前面
constexpr和常量表达式
常量表达式是指不会改变并且在编译过程就能得到计算结果的表达式。
C++11允许将变量声明位constexpr类型以便由编译器来验证变量的值是否是一个常量表达式。
一般来说,如果你认定变量是一个常量表达式,那么就吧它声明成constexpr类型。
自定义类、IO库、string类型都不是字面值类型,不能被定义成constexpr,一个constexpr指针的初始值必须是nullptr或者0,或者是存储于某个固定地址中的对象。
*在constexpr声明中如果定义了一个指针,限定符constexpr仅对指针有效,与指针所指的对象无关:
const int *p = nullptr; //p是一个指向整型常量的指针
constexpr int *q = nullptr; //q是一个指向整数的常量指针
2.5 处理类型
类型别名
- typedef double wages;
- using SI = Sales_item;
auto类型说明符
它能让编译器去分析表达式的类型从而确定变量的类型,一条声明语句只能由一个基本数据类型。
auto一般会忽略掉顶层const,同时底层const则会保留下来。
decltype类型指示符
它的作用是选择 并返回操作数的数据类型,不实际计算表达式的值: decltype (f()) sum = x;
编译器并不实际调用函数f,而是使用当调用发生时f的返回值类型作为sum的类型。
如果给变量加上了一层或多层括号,编译器就会把它当成是一个表达式。变量是一种可以作为赋值语句左值的特殊表达式,赋值是会产生引用的一类典型表达式,引用的类型就是左值的类型,所以这样的decltype就会得到引用类型,而非变量的类型。
2.6 自定义数据结构
预处理命令:在编译前执行的一段程序,可以部分地改变我们所写的程序。
头文件保护符 #ifdef
当且仅当变量已定义时为真 #ifndef
当且仅当变量未定义时位真
一旦检测结果位真,则执行后续操作直至遇到#endif
为止。
#ifndef SALES_DATA_H
#define SALES_DATA_H
#include <string>
...
#endif