1. 复合类型声明的理解
一条声明语句由基本类型和声明符组成。基本数据类型的声明符等同于变量名。而如果对变量名予以修饰,就会得到一个复合类型,常见的有指针和引用。
在声明指针的时候,将指针符号与基本数据类型或变量名连着写的人都有。
int *i;
int* j;
基于声明语句的结构,我更习惯于第一种,因为我认为声明符连在一起更顺眼些。这种感觉在连续声明指针的时候尤为明显。当然,这是个人喜好。
int *i, *j; // i, j是指针
int *i, j; // i是指针,j是整型
2. 引用与指针的底层原理
引用和指针在编译成汇编后,均表现为一块存储着其所绑定对象地址的内存空间。然而二者在进行操作时却有着截然不同的表现。
引用在定义后无法绑定其他对象,因为一切对引用的操作无法直接作用在该内存空间上,而是会将该内存空间存储的对象地址读到寄存器中,再对该地址指向的对象进行进一步读写操作。这就是引用的本质。
而对指针的操作都是直接对该内存空间本身进行操作。只有当使用*
解引用符时,指向元素的地址才会被拿到寄存器上操作(即操作该指针指向的对象)。
下面我写两个函数,编译成汇编代码。
void refrence(){
int c = 1;
int &cc = c;
cc = 2;
}
void pointer(){
int c = 1;
int *d = &c;
d = 0x0;
*d = 5;
}
3. 指针细节
- 指针的类型应当与其指向变量的类型一致,否则会引发错误。例如:
double j = 114.514;
int *i = j; // error
- 指针初始化时,可以给指针初始化为
nullptr
(C++ 11),可以初始化为NULL
(需包含<cstdlib>
),也可以让其等于0,但是不能给其赋值一个为0的基本类型。
4. void*
作为一种特殊的指针,void*
也可以用于指向一块内存空间。不同的是,程序无法通过该指针知晓那块内存空间的数据类型,继而无法做出有效的读写操作。
struct A{
void out(){} //do something ...
}
void *a = new A();
a.out(); // error
如果程序员知道一个void*
指针指向对象的类型,可以用将该指针进行类型转换,进而可以操纵这个对象。
A* b = a;
b.out(); // success
5. 指针引用
引用不是对象,因此不存在引用指针这种东西。但指针是对象,因此一个引用可以绑定一个指针,就像下面这样:
int *p = nullptr;
int *&r_p = p; // 指针引用
指针引用的声明符看上去很复杂。cpp primer讲了一种理解方式我觉得很好。判断一个变量类型,要从它声明语句最接近变量名的那个符号开始,自右向左读。在上面的例子里,r_p
是变量名,最近的符号为&
,因此它是一个引用,一个与int *
类型做绑定的引用,以此类推。