C++可以说是成也指针、败也指针。依靠着指针,我们可以灵活地操控变量内存地址,实现很多独有的功能。但也正因为指针,尤其是使用不当的时候会产生许多的问题。导致许多工程师对于 C++以及指针深恶痛绝,以至于 C++之后的许多语言都摒弃了指针的设计,比如 Java 和 Python。
我们先把头疼的内容放一放,先从一些简单的概念开始。
首先要明确的是指针是一个变量,它特殊的点在于虽然同样是变量,它存储的并不是值,而是一个内存地址。内存地址顾名思义就是存放在内存当中的位置,对于非指针的变量, 我们也可以使用&
操作符去获取它的地址。这就是为什么我们使用scanf
在读取变量的时候,需要在变量名之前加上一个&
符号。
int a;
scanf("%d", &a);
目的就是为了将 a 变量的地址传给scanf
函数,从而将屏幕当中读取到的内容填写到 a 变量对应的地址当中。
我们也可以直接输出一个变量的地址,但输出结果是一个十六进制的数,代表一个内存位置。如果大家学过汇编或者是了解过底层的话,应该不陌生。这个输出的结果是给机器看的,人类无法读懂。
int a;
cout << &a << endl;
指针和普通变量不同,它存储的值是地址。所以在声明指针的时候,也会有一点细小的区别。我们通过*符号创建指针,*运算符称为间接值(indirect value)或解除引用(dereferencing),现在理解这两个概念可能有些费劲,没关系我们可以先放一放。只许看记住使用*创建指针即可,*写在类型和变量名中间,如:
int * p;
这样我们就创建了一个int
型的指针,它的名字叫做 p。关于*的位置,有些人喜欢紧跟着变量类型,有些人喜欢紧跟着变量名。其实都可以,看个人喜好。传统上来说 C 程序员喜欢后者,突出 ptr 是一个指针。
int *ptr;
C++程序员更喜欢前者,突出是一个int
型的指针:
int* ptr;
这两种都可以,对于编译器来说没有任何区别。但是要注意的是,每一个指针变量都需要一个*:
int a, *ptr;
前面说了,由于指针的值是一个地址,所以我们在对指针进行初始化或者赋值的时候,就需要用到取地址符。
int a = 3;
int *p = &a; // 获取了a的地址
当我们有了指针变量之后,我们可以使用*来访问它指向的内存地址的值。
int a = 3;
int *p = &a;
cout << *p << endl; //output: 3
要注意的是,由于指针 p 指向 a 的地址,所以当我们通过*符号修改了 p 指向的值之后,a 的值一样会发生变化。
*p = 5;
cout << a << endl; //output: 5
正因为指针有这样的特性,所以使用的时候千万小心……