C++很基础的易混淆点一

2.1.1 :

  • C++标准规定的各种算术类型的尺寸的最小值, 同时允许编译器赋予这些类型更大的尺寸. 比如char的最小尺寸为8位

  • 执行浮点数运算选用double ,这是因为float 通常精度不够而且双精度浮点数和单精度浮点数的计算代价相差无儿。事实上, 对于某些机器来说,双精度运算甚至比单精度还快

2.1.2 :

  • 当我们赋给无符号类型一个超出它表示范围的值时,结果是初始值对无符号类型表示数值总数取模后的余数。例如, 8 比特大小的 unsigned char 可以表示0 至
    255 区间内的值,如果我们赋了一个区间以外的值,则实际的结果是该值对256
    取模后所得的余数。因此,把 -1 赋给8 比特大小的 unsigned char 所得的结果
    是255

  • 当我们赋给带符号类型一个超出它表示范围的值时,结果是未定义的( undefined )。
    此时, 程序,可能继续工作、可能崩溃,也可能生成垃圾数据。

  • 如果表达式里既有带符号类型又有无符号类型, 当带符号类型取值为负时会出现异
    常结果, 这是因为带符号数会自动地转换成无符号数。例如,在一个形如 a*b 的式子
    中,如果a = -1 , b = 1 ,而且a 和b 都是int ,则表达式的值显然为- 1. 然而,如
    果a 是int , 而b 是unsigned , 则结果须视在当前机器上int 所占位数而定。在32环境里,因为2的32次方是4294967296, 所以a*b结果是4294967295

. . .

2.2.1 :

  • 如果是内置类型的变量未被显式初始化,它的值由定义的位置决定。定义于任何函数
    体之外的变量被初始化为0 。然而如6. 1. 1 节(第1 85 页)所示, 一种例外情况是,定义
    在函数体内部的内置类型变量将不被初始化。一个未被初始化的内置类型
    变量的值是未定义的(参见2. 1. 2 节, 第33 页) ,如果试图拷贝或以其他形式访问此类值
    将引发错误

  • 定义于函数体内的内置类型的对象如果没有初始化,值未定义。类的对象
    如果没有显式地初始化,则由类确定。

2.2.2 :

为了支持分离式编译, C++语言将声明和定义区分开来。声明( declaration ) 使得名字
为程序所知, 一个文件如果想使用别处定义的名字则必须包含对那个名字的声明。而定义
( definition ) 负责创建与名字关联的实体。
变量声明规定了变量的类型和名字, 在这一点上定义与之相同。但是除此之外,定义
还申请存储空间,也可能会为变量赋一个初始值。
如果想声明一个变量;而非定义它,就在变量名前添加关键字extern ,而且不要显式
地初始化变量:

1
2
extern int i ; // 声明i 而非定义i
int j ; / / 声明并定义j

任何何包含了显式初始化的声明即成为定义。我们能给由extern 关键字标记的变量赋
一个初始值,但是这么做也就抵消了extern 的作用。extern 语句如果包含初始值就不
再是声明,而变成定义了:
extern doub1e pi = 3 . 1416 ; // 定义

变量能且只能定义一次, 但可以多次声明.

在函数体内部,如果试图初始化一个由extern 关键字标记的变量, 将引发错误.

2.2.3 :

  • C++也为标准库保留了一些名字。用户在自定义的标识符中不能连续出现两个下划线,也不能以下划线紧连大写字句开头。此外,定义在函数体外的标识稍不能以下划线
    开头。 比如: int _ = 3;是合法的

2.3.1 :

  • 其他所有引用的类型都要和与之绑定的对象严格匹配。而且,引用只能绑定在对象上,而不能与
    字面值或某个表达式的计算结果绑定在一起, 相关原因将在2 .4 . 1 节详述:
1
2
3
int &refVa14 = 10 ; //错误· 引用类型的初始值必须是一个对象
double dval = 3.14 ;
int &refVa15 = dva1 ; // 错误: 此处引用类型的初始位必须是int 型对象

2.3.2 :

  • 因为引用不是对象, 没有实际地址,所以不能定义指向引用的指针。

2.4 :

默认状态下, const 对象仅在文件内有效

当以编译时初始化的方式定义一个const 对象时,就如对bufSize 的定义一样:

const int bufSize = 512; //输入缓冲区大小

编译器将在编译过程中把用到该变量的地方都替换成对应的值。也就是说,编译器会找到
代码中所有用到 bufSize 的地方,然后用512 替换。
为了执行上述替换, 编译器必须知道变量的初始值。如果程序包含多个文件,则每个
用了const 对象的文件都必须得能访问到它的初始值才行。要做到这一点,就必须在每
一个用到变量的文件中都有对它的定义(参见2.2.2 节, 第4 1 页)。为了支持这一用法,
同时避免对同一变量的重复定义,默认情况下, const 对象被设定为仅在文件内有效。
多个文件中出现了同名的const 变量时,其实等同于在不同文件中分别定义了独立的变量。
某些时候有这样一种const 变量,它的初始值不是一个常量表达式,但又确实有必
要在文件间共享。这种情况, 我们不希望编译器为每个文件分别生成独立的变量。相反,
我们想让这类const 对象像其他(非常量)对象一样工作,也就是说,只在一个文件中
定义const ,而在其他多个文件中声明并使用它。
解决的办法是,对于 const 变量不管是声明还是定义都添加extern 关键字, 这样
只需定义一次就可以了:

1
2
3
4
// file 1 . cc 定义并初始化了一个常量,该常量能被其他文件访问
extern const int bufSize = fcn();
// file 1 . h 头文件
extern const int bufSize ; /1 与f ile 1. cc 中定义的bufSize 是同一个

如上述程序所示,

  • file 1. cc 定义并初始化了bufSize 。因为这条语句包含了初始值,
    所以它(显然〉是一次定义。然而,因为bufSize 是个常量,必须用extern 加以限
    定使其被其他文件使用。
  • file 1. h 头文件中的声明也由extern 做了限定,其作用是指明bufSize 并非
    file 1独有,它的定义将在别处出现。

2.4.1

初始化和对const 的引用

2.3.1节(第46 页)提到, 号| 用的类型必须与其所引用对象的类型→致,但是有两个
例外。第一种例外情况就是在初始化常量引用时允许用任意表达式作为初始值,只要该表
达式的结果能转换成(参见2.1 .2 节,第3 2 页)引用的类型叩可。尤其,允许为一个常量
引用绑定非常量的对象、字面值, 甚至是个-般表达式:

1
2
3
4
5
int i= 42 ; .
const int &r1 = i ; // ft许将const int &r1 定到一个普通int 对象上
const int &r2 = 42; // 正确r1 是一个常量引用
const int &r3 = r1 * 2 ; // 正确r3 是一个常量引用
int &r4 = r1 * 2 ; // 错误r4 是一个普通的非常量引用

要想、理解这种例外情况的原因,陆简单的办法是弄清楚当一个常量引用被绑定到另外一种
类型上时到底发生了什么:

1
2
doub1e dval = 3 . 14 ;
const int &ri = dva1 ;

此处ri 引用了一个int 型的数。对口的操作应该是整数运算,但dval 却是一个双精
度浮点数而非整数。因此为了确保让rl 绑定一个整数,编译器把上述代码变成了如下
形式:

1
2
const int temp = dval; 1/ 由双精度浮点数生成一个临时的整型常量
const int &ri = temp ; 11 让rl 绑定这个临时受

在这种情况下, ri 绑定了一个临时量对象。所谓临时量对象就是当编译器
而要一个空间来暂存表达式的求值结果时临时创建的一个未命名的对象。c++程序员们常
常把临时量对象简称为临时量。
接下来探讨当ri 不是常量时,如果执行了类似于上面的初始化过程将带来什么样的
后果。如果且不是常量,就允许对ri 赋值,这样就会改变ri 所引用对象的值。注意,
此时绑定的对象是一个临时量;而dvalo 程序员既然让rl 引用dval , 就肯定想通过
ri 改变dval 的值,否则干什么要给ri 赋值l呢?如此看来, 既然大家基本上不会想着把
引用绑定到临时量上, c++语言也就把这种行为归为非法。

2.5.2

  • 和原来另一些只对应一种特定类型的说明符(比如double) 不|司. auto 让编译器通
    过初始值来推算变量的类型。显然. auto 定义的变量必须有初始值:

  • 使用auto 也能在一条语句中声明多个变量。因为一条声明语句只能有一个基本数据
    类型,所以该语句中所有变量的初始基本数据类型都必须一样:

1
2
3
int i = 0; 
const int ci = i;
auto &n = i, *p = &ci // 错误 : i 的类型是int 而& ci 的类型是const int