指针地址与左右值

C++中,免不了要与指针和地址打交道,但是看着繁多的*&有时又容易混乱。

*&

简单来说*在声明时代表的是变量所处地址的指针,可以理解为幸运大转盘的箭头,指向幸运大转盘的一个位置,也就是变量所处的地址。而&表示的就是取该变量的地址,也就是大转盘针头指着的地方。

1
2
3
4
5
6
void main() {
int num = 1;
cout << &num << endl;
int* p = &num;
cout << p << endl;
}

如上所示,&num打印出来后就是该变量所处的地址信息,也许是40013ac7H,而第四行的int*则是声明了一个指向num地址的指针,这里容易混淆,为何赋值表达式左右是*&,但意义却不一样?其中*更像一个定位的标志,告诉你该指针现在指向什么地方,比如在连续空间(如数组)中,可以通过对指针进行加法操作:p++来进行顺序访问数组元素,也就是让本指向数组中某个位置的箭头往后移动一位。

1
2
3
4
5
6
void main() {
int num = 1;
int* p = &num;
cout << *p << endl;
cout << &p << endl;
}

其中如果在指针变量前加入*则可以获得指针指向的存储空间的内容,这里就是1,而再在前方加上&则可以获取到指针在内存中的地址,毕竟哪怕是一个箭头既然声明了,那么也需要相应的内存空间进行存储。
如果我们再次进行*操作:

1
2
3
4
5
6
void main() {
int num = 1;
int* p = &num;
cout << *&p << endl;
cout << **&p << endl;
}

其中*&p&p是指针(也就是箭头)的地址,那么刚才说了,加上*可以获得箭头所指的内容,那么实际上箭头所指的就是num的地址,也就是&num。如果再加上*那么就能取到&num地址所存放的内容,也就是1了。

左值与右值

左值很好解释:

1
2
3
4
void main() {
int x = 'x';
int num = 1;
}

这些在内存中占用空间并拥有地址的值,都是左值,那么什么是右值?

1
2
3
4
void main() {
int num;
num = 1 + 2;
}

其中1 + 2就是右值,它只是一个临时的表达式,并没有自己的地址,同时如果去取地址也只会得到报错。

那么右值有什么用呢?在传统的c++中,一行普通的代码可能如下:

1
2
3
4
5
6
class A {}
void main() {
A a();
A b();
a = b;
}

这时候我们会有什么样的操作呢?我们可能得到了b的复制(当然取决于如何定义operator=),但是这样呢?

1
2
3
4
5
6
class A {}
void main() {
A a();
A b();
a = b + b;
}

这时候b + b明显的是一个右值,我们是否可以得到复制呢?参数列表中我们往往会声明一个A& a的参数,但是刚才有说过,右值往往不拥有自己的地址,那么我们的代码就只能报错了吗?(假定已经实现了operator+),不尽然:

1
2
3
4
5
6
7
8
class A {}
A & A::operator=(A&& a) {}

void main() {
A a();
A b();
a = b + b;
}

我们可以看到,参数列表中的变量类型为A&&,这时候我们就可以简单的捕获到右值了。
右值引用的加入,使得我们可以更好的分辨出移动与复制,也填补了C++中的空白。