C++指针的使用(非常详细,新手必看)
指针作为 C++ 世界中一种特殊的数据访问方式,它的灵活的使用方式使它在 C++ 世界中显得威力无比。然而,正是因为它的灵活,也让它成为 C++ 初学者最难掌握的技能。
既然是数据,指针也可以参与运算,包括算术运算、关系运算和赋值运算。在这些运算中,指针的算术加减运算是最常用的,它们允许我们改变指针所指向的内存位置。
当我们说一个指针指向某个内存位置时,意味着它存储了那个内存位置的地址。通过指针的加减运算,我们可以访问指针起始位置附近的内存。这种偏移使我们能够灵活地访问连续内存区域的数据。
例如,在数组的应用中,数组名代表数组在内存中的首地址。通过将数组的首地址赋给指针,然后对指针进行加减运算,就可以遍历数组中的所有元素。例如:
而在对指针进行加 1 运算后,指针指向的地址变为 0016FA3C,也就是向地址增大的方向偏移了 4 字节,指向了数组中的第二个数据,*pIndex 输出的值自然也就变成了2。
这里读者可能会感到奇怪,既然对指针进行的是加 1 运算,为什么指针指向的地址却增加了 4 个字节?
这是因为指针的加减运算与它所指向的数据类型密切相关。指针加 1 或者减 1,会使指针指向的地址增加或者减少一个对应的数据类型的字节数。
例如,上述代码中的 pIndex 指针指向的是 int 类型的数据,所以它的加 1 运算使地址增加了 4 字节,也就是一个 int 类型数据占用的字节数。
同理,对于指向 char 类型数据的 char* 类型指针,加 1 会使指针偏移 1 字节;而对于指向 double 类型数据的 double* 类型指针,加 2 会使指针偏移 16(8×2)字节。
指针偏移示意图如下图所示。

图 1 指针运算引起的指针偏移
除指针的加减运算外,常用的还有指针的关系运算。
指针的关系运算通常用“==”或“!=”来判断两个相同类型的指针是否相等,也就是判断它们是否指向同一地址上的数据。
这类运算常用在条件判断或循环结构中,以下是一个示例:
此外,指针变量常和 nullptr 关键字进行相等比较,以判断指针是否已经被初识化并指向有效的内存位置,即判断指针是否有效。
虽然我们提倡在定义指针的同时进行初始化,但有时在定义指针时,可能没有合适的初始值可以给它赋值。如果让指针保持最开始的随机值,又会产生不可预见的结果。在这种情况下,指针可以被初始化为 nullptr,表示该指针尚未被初始化,处于不可用的状态。稍后,当有合适的值时,再将真正有意义的值赋给指针,这时指针的值将不再是 nullptr,意味着该指针处于可用状态。
因此,将指针与 nullptr 进行相等比较,是判断该指针是否可用的常用手段。以下是一个典型的示例:
C++指针的运算
从本质上讲,指针是一种特殊的数据类型,它表示的是内存地址。与我们常见的数值和字符数据不同,指针指向内存中的具体位置。既然是数据,指针也可以参与运算,包括算术运算、关系运算和赋值运算。在这些运算中,指针的算术加减运算是最常用的,它们允许我们改变指针所指向的内存位置。
当我们说一个指针指向某个内存位置时,意味着它存储了那个内存位置的地址。通过指针的加减运算,我们可以访问指针起始位置附近的内存。这种偏移使我们能够灵活地访问连续内存区域的数据。
例如,在数组的应用中,数组名代表数组在内存中的首地址。通过将数组的首地址赋给指针,然后对指针进行加减运算,就可以遍历数组中的所有元素。例如:
int nArray[3] = { 1, 2, 3 }; // 定义一个数组 int* pIndex = nArray; // 将数组的起始地址赋给指针 pIndex cout << "指针指向的地址是:" << pIndex << endl; // 输出指针指向的地址 cout << "指针所指向的数据的值是:" << *pIndex << endl; // 输出这个位置上的数据 pIndex++; // 对指针进行加运算,使其指向数组中的下一个值 cout << "指针指向的地址是:" << pIndex << endl; // 输出指针指向的地址 cout << "指针所指向的数据的值是:" << *pIndex << endl; // 输出数据这段程序执行后,可以得到这样的输出:
指针指向的地址是:0016FA38
指针所指向的数据的值是:1
指针指向的地址是:0016FA3C
指针所指向的数据的值是:2
而在对指针进行加 1 运算后,指针指向的地址变为 0016FA3C,也就是向地址增大的方向偏移了 4 字节,指向了数组中的第二个数据,*pIndex 输出的值自然也就变成了2。
这里读者可能会感到奇怪,既然对指针进行的是加 1 运算,为什么指针指向的地址却增加了 4 个字节?
这是因为指针的加减运算与它所指向的数据类型密切相关。指针加 1 或者减 1,会使指针指向的地址增加或者减少一个对应的数据类型的字节数。
例如,上述代码中的 pIndex 指针指向的是 int 类型的数据,所以它的加 1 运算使地址增加了 4 字节,也就是一个 int 类型数据占用的字节数。
同理,对于指向 char 类型数据的 char* 类型指针,加 1 会使指针偏移 1 字节;而对于指向 double 类型数据的 double* 类型指针,加 2 会使指针偏移 16(8×2)字节。
指针偏移示意图如下图所示。

图 1 指针运算引起的指针偏移
除指针的加减运算外,常用的还有指针的关系运算。
指针的关系运算通常用“==”或“!=”来判断两个相同类型的指针是否相等,也就是判断它们是否指向同一地址上的数据。
这类运算常用在条件判断或循环结构中,以下是一个示例:
int nArray[3] = { 1, 2, 3 }; // 定义一个数组 int* pIndex = nArray; // 将数组的起始地址赋给指针 pIndex int* pEnd = nArray + 3; // 计算数组的结束地址并赋给 pEnd while (pIndex != pEnd) // 在 while 的条件语句中判断两个指针是否相等 { // 也就是判断当前指针是否已经偏移到结束地址 cout << *pIndex << endl; // 输出当前指针指向的数据 ++pIndex; // 对指针进行加1运算 // 使其偏移到下一个内存位置,指向数组中的下一个数据 }在以上这段代码中,我们使用表示数组当前位置的指针 pIndex 与表示结束位置的指针 pEnd 进行相等与否的比较:
- 如果不相等,则意味着 pIndex 尚未偏移到数组的结束位置,可以继续对 pIndex 进行加 1 运算,使其偏移至下一个位置,指向数组中的下一个元素;
- 如果相等,则意味着 pIndex 正好偏移到数组的结束位置,while 循环已遍历了整个数组,循环可以就此结束。
此外,指针变量常和 nullptr 关键字进行相等比较,以判断指针是否已经被初识化并指向有效的内存位置,即判断指针是否有效。
虽然我们提倡在定义指针的同时进行初始化,但有时在定义指针时,可能没有合适的初始值可以给它赋值。如果让指针保持最开始的随机值,又会产生不可预见的结果。在这种情况下,指针可以被初始化为 nullptr,表示该指针尚未被初始化,处于不可用的状态。稍后,当有合适的值时,再将真正有意义的值赋给指针,这时指针的值将不再是 nullptr,意味着该指针处于可用状态。
因此,将指针与 nullptr 进行相等比较,是判断该指针是否可用的常用手段。以下是一个典型的示例:
int* pInt; // 定义一个指针,这时的指针是一个随机值,指向随机的一个内存地址 // 将指针赋值为 nullptr,表示指针还没有合适的值,处于不可用的状态 pInt = nullptr; // ... int nArray[10] = {0}; pInt = nArray; // 将数组首地址赋值给指针 if (nullptr != pInt) // 判断指针是否已经完成初始化,处于可用状态 { // 指针可用,开始使用指针访问它指向的数据 }因为通过指针可以直接访问它所指向的内存,所以对尚未初始化的指针的访问可能带来非常严重的后果。将指针与 nullptr 进行相等比较,可以有效地避免指针的非法访问。虽然在业务逻辑上这不是必需的,但这样做可以使程序更加健壮。这是一条非常好的编程经验。