引用和指针
引用(左值引用)
引用为对象起了另外一个名字,引用必须初始化,引用会和被引用对象的值绑到一起(而不是拷贝),引用无法重新绑定到一个新的对象。
对引用的操作相当于对绑定对象的操作,但是引用本身不是对象,所以不能定义对引用的引用。可以用引用初始化引用,相当于把新的引用绑定到给它赋值的引用所指的对象。
引用只能绑定到对象上,不能绑定常量或者表达式。
引用类型和绑定对象的类型要严格匹配。
例:
int a=1;
int &b=a; // b是a的别名或者说b指向a
int &c; // 错误,需要初始化
b=2; // 把2赋给b指向的对象,即把2赋给a
int &d=b; // 将d绑定到b所指向的对象,这里不是引用的引用,所以正确
int &&e; // 错误,引用的引用
int &f=1; //错误,不能绑定常量
double aa=1.01;
int &g=aa; // 错误,类型要严格匹配
指针
指针本身是一个对象,允许对指针赋值和拷贝,指针无需再定义时赋值。
指针存放某个对象的地址,想要获得地址,需要使用取地址符(&),引用不是对象没有地址,所以不能定义指向引用的指针。
例:
int a=1;
int *b=&a; // b存放a的地址或者说b是指向a的指针
int *c=b; //c指向b
指针的类型必须和指向对象的类型匹配。
指针的值只会有下面几种情况:
- 指向一个对象
- 指向对象所占空间的下一位置
- 空,没有指向任何对象
- 无效指针,任何试图访问无效指针的操作都会引发错误
如果指针指向了一个对象,可以通过解引用(*)来访问对象,如果对解引用的结果赋值,实际上就是给指针所指对象赋值。
空指针不指向任何对象,得到空指针的方法就是用nullptr来初始化指针,它可以被转换成任何其他的指针类型,另一种方法就是用 0 或者 NULL 来初始化。
赋给指针的值必须是地址,不能直接把变量赋给指针,即使变量为 0。
例:
int *a=0;
int *a=NULL; // 等价于 int *a=0;
int *a=nullptr; // 等价于 int *a=0;
int zero=0;
a=zero; // 错误。不能直接把变量赋给指针
最好初始化所有指针。
void *
指针可以存放任意类型对象的地址,但是不能对所指对象直接操作。(应该就是不能解引用,只能用来存地址,不能通过地址访问对象)
复合类型声明
可以将修饰符和变量名写在一起,例如 int *a
;也可以将类型名和修饰符写一起,例如 int* a
指向指针的指针,指针是内存中的对象,可以把指针的地址存放到另一个指针中,也就是指向指针的指针,要访问指向指针的指针指向的对象,需要两次解引用。
同理还有指向指针的引用,离变量最近的符号对变量的类型有最直接的影响(从右到左阅读)
例如
int *p; // int 指针
int *&a=p; // 指向p指针的引用
补充
某些符号有多重含义:
int a=1;
int &b=a; // & 紧随类型名出现,因此是声明的一部分,是一个引用
int *c; // * 紧随类型名出现,因此是声明的一部分,是一个指针
c=&a; // & 出现在表达式中,是一个取地址符
*c = a // * 出现在表达式中,是一个解引用符
int &d=*c // & 是声明的一部分,是引用,* 出现在表达式中,是一个解引用符
在声明中,紧随类型名出现的 *
和 &
是指针和引用;在表达式中出现的*
和 &
是解引用和取地址。