复合类型
复合类型是指基于其他类型定义的类型:如引用和指针等
一条声明语句由一个基本数据类型和紧随其后的一个声明符列表组成。
引用
引用为对象起了另外一个名字,引用类型引用另外一种类型,通过将声明符写成&d的形式来定义引用类型,其中d是声明的变量名
引用必须被初始化:一般在初始化变量时,初始值会被拷贝到新建的对象中。然而定义引用时,程序把引用和它的初始值绑定在一起,而不是将初始值拷贝给引用,一旦初始化完成,引用将和它的初始值对象一直绑定在一起。
引用并非对象,没有实际地址。相反的,它只是为一个已经存在的对象所起的另外一个名字。因此,不能定义引用的引用。
定义了一个引用之后,对其进行的所有操作都是在与之绑定的对象上进行的。
引用只能绑定在对象上,而不能与字面值或某个表达式的计算结果绑定在一起。且引用的类型要和与之绑定的对象严格匹配。
指针
指针是指向另外一种类型的复合类型。实现了对其他对象的间接访问。
指针本身就是一个对象,允许对指针赋值和拷贝,而且在指针的生命周期内它可以先后指向几个不同的对象。
指针无须在定义时赋初值。在作用域内定义的指针如果没有被初始化,也将拥有一个不确定的值。
double dp, *dp2; // dp2是指向double型对象的指针,dp是double型对象。
指针存放某个对象的地址,要想获取该地址,需要使用取地址符(&)
int ival = 42; int *p = &ival; // p存放变量ival的地址,或者说p是指向变量ival的指针。
定义指针时指针的类型要和它所指向的对象严格匹配。
不能定义指向引用的指针。
double dval; double *pd = &dval; // 初始值是double型对象的地址 double *pd2 = pd; // 初始值是指向double对象的指针
指针的值(即地址)应属下列4种状态之一:
1、指向一个对象。
2、指向紧邻对象所占空间的下一个位置。
3、空指针,意味着指针没有指向任何对象。
4、无效指针,也就是上述情况之外的其他值。
如果指针指向一个对象,则允许使用解引用符(操作符*)来访问该对象。
对解引用的结果赋值,实际上也就是给指针所指的对象赋值。
空指针
空指针不指向任何对象,在试图使用一个指针之前代码可以首先检查它是否为空。
得到空指针最直接的方法就是用字面值nullptr来初始化指针。nullptr是一种特殊类型的字面值,它可以被转换成任意其他的指针类型。
赋值
引用本身并非一个对象,一旦定义了引用,就无法令其在绑定到另外的对象,之后每次使用这个引用都是访问它最初绑定的那个对象。
指针和其他非引用变量一样,给指针赋值就是令它存放一个新的地址,从而指向一个新的对象。
赋值永远改变的是等号左侧的对象。
两个指针存放的地址值相同有三种可能:
1、它们都为空。
2、都指向同一个对象
3、都指向了同一个对象的下一个地址。
需要注意的是:一个指针指向某对象,同时另一个指针指向另外对象的下一个地址,此时也有可能出现这两个指针值相同的情况,即指针相等。
void*指针
void*是一种特殊的指针类型,可用于存放任意对象的地址。对于存放的地址到底是一个什么类型的对象并不了解。
不能直接操作void*指针所指的对象。
以void*的视角来看内存空间也就仅仅是内存空间,没办法访问内存空间中所存的对象。
复合类型的声明
变量的定义包括一个基本数据类型和一组声明符。在同一条定义语句中,虽然基本数据类型只有一个,但是声明符的形式却可以不同。类型修饰符是声明符的一部分。
int = 1024, *p = &i, &r = i; // i是一个int型的数,p是一个int型指针,r是一个int型引用
指向指针的指针
指针是内存中的对象,像其他对象一样也有自己的地址,因此允许把指针的地址再存放到另一个指针中。
int ival = 1024; int *pi = &ival; // pi指向一个int型的数 int **ppi = π // ppi指向一个int型的指针
解引用int型指针会得到一个int型的数,解引用指向指针的指针会得到一个指针。
指向指针的引用
引用本身不是一个对象,因此不能定义指向引用的指针。
但指针是对象,所以存在对指针的引用
int i = 42; int *p; // p是一个int型指针 int *&r = p; // r是一个对指针p的引用 r = &i; // r引用了一个指针,因此给r赋值&i就是令p指向i *r = 0; // 解引用r得到i,也就是p指向的对象,将i的值改为0
要理解r的类型到底是什么,需要从又向左阅读r的定义。&表示r是一个引用。*确定r引用的是一个指针。int表示r引用的是一个int指针。
Tips:
1、指针本身就是一个对象,允许指针之间的拷贝和赋值,也可以在其生命周期指向不同的对象。而引用不行
2、指针无需在定义时赋值。
3、引用必须被初始化
4、引用并非对象,没有实际地址。相反的,它只是为一个已经存在的对象所起的另外一个名字。
5、void*可以接收任意类型的对象
6、注意&和*的多重意义。