指针的定义 Ⅰ

上节提到了,指针是存放地址的变量。下面我们来学习如何声明并定义一个指针:

基类型* 指针名 初始化器;

比如:

int* address{&c};

我来解释一下。当你声明并定义一个指针的时候,你需要提供一个称为 基类型 的东西,此时这个指针存储的地址只能基类型 这个类型变量的地址。比如:

int c{76};
int d{42};
int* address{&c}; // address 的基类型为 int*,所以它只存储 int 型变量的地址
address = &d;     // 将 int 型变量 d 的地址赋值给 address
short e{1};
address = &e;     // 错误:address 不能存储 short 型变量的地址

当一个指针的基类型为 T 时,称这个指针的类型为指向 T 的指针类型。类似地,如果一个指针存储了变量 a 的地址,则称这个指针指向变量 a

int c{76};
int* address{&c}; // int* 为指向 int 的指针类型
                  // address 为指向变量 c 的指针

如果用图示画出来,就是:

pointer

事实上,地址本身就包含了基类型的信息。在后文中,我有时也会称一个地址的基类型为某某某,所以不必纠结于这些用词。

关于初始化

指针也是变量,因此它的声明方式和普通变量一致:初始化器也是可选的。因此你完全可以

int* a;  // 指向 int 的指针类型
char* b; // 指向 char 的指针类型

这样不写初始化器。但是问题来了,如果不写初始化器,这样的表达式:

*a = 3;
*b = 'A';

会造成什么后果呢?答案是未知的。因为你根本不知道没有初始化时,a 这个指针里存放的到底是那个地址,*a 是个什么东西根本无法得知。如果 a 或者 b 指向了非法的存储空间,然后你又对这片存储空间进行了修改,可能会导致很严重的后果(甚至可能影响到操作系统)。所以我们认为不初始化指针是非常危险的

为了解决这个问题,C++ 提供了一个称为空指针(Null pointer)的字面量:nullptr。它可以转换为指向任何类型的指针类型:

int* a{nullptr};
char* b{nullptr};

尽管此时访问 *a*b 仍然是非法的(会导致运行时错误),但是它不会造成任何危险。所以,我在这里建议:定义指针时必须初始化,若无合适的初始化值则初始化为 nullptr

nullptr 是指针类型的零值,即零初始化时采用的值。

事实上,nullptr 不是指针类型的字面量;它的类型为 空指针类型 std::nullptr_t

关于类型标识

根据之前类型标识的概念,我们可以得知指针类型的类型标识是 T*,其中 T 是基类型。不过由于指针类型属于复合类型,所以在同时声明并定义多个指针时,倘若

int* a, b;

这样写,只有 a 是一个 int* 类型的指针,而 b 只是一个 int 型变量。这一点和数组的声明很相似,但是不容易看出。如果你想将 ab 都声明为指针,你需要

int *a, *b;

这样来做。看上去有一点别扭——其中 * 不是变量名的一部分,而是表示引入的名字是一个指针。这种奇怪的语法是 C 语言遗留下来的糟粕,不要太纠结就是。所以这里我建议,声明并定义指针时尽量一句一个

int* a{nullptr};
int* b{nullptr};

这样就好很多。

关于指针声明符中 * 的位置,附录中提供了更多的讨论与解释。

最近更新:
代码未运行