C++: 指针算术进阶
最后更新:2026-08-26
第23课我们学了指针算术的基础(
p + n、p - q)。但指针算术还有很多细节——比如指针能比较大小吗?
void*指针是什么?指针的指针又是什么?
1. 指针比较
(1) 1.1 指针能比较大小吗?
能! 但只有指向同一个数组的指针比较才有意义。
▶ 示例 2:基础编程实践(难度⭐)
#include <iostream>
int main() {
int arr[5] = {10, 20, 30, 40, 50};
int* p1 = &arr[0];
int* p2 = &arr[3];
if (p1 < p2) { // ✅ p1 指向的元素在 p2 前面
std::cout << "p1 在 p2 前面" << std::endl;
}
if (p2 - p1 == 3) { // ✅ 两个指针相隔 3 个元素
std::cout << "p1 和 p2 相隔 3 个元素" << std::endl;
}
return 0;
}
输出:
p1 在 p2 前面
p1 和 p2 相隔 3 个元素
💡 重点: 指针比较的是地址大小(即元素在数组中的位置)。
(2) 1.2 指针相等比较
#include <iostream>
int main() {
int x = 5;
int* p1 = &x;
int* p2 = &x;
if (p1 == p2) { // ✅ 两个指针指向同一个变量
std::cout << "p1 和 p2 指向同一个变量" << std::endl;
}
int y = 10;
int* p3 = &x;
int* p4 = &y;
if (p3 != p4) { // ✅ 两个指针指向不同的变量
std::cout << "p3 和 p4 指向不同的变量" << std::endl;
}
return 0;
}
2. void* 指针
(1) 2.1 什么是 void* 指针?
void* 是通用指针——可以指向任何类型的数据。
#include <iostream>
int main() {
int x = 5;
double y = 3.14;
char c = 'A';
void* p1 = &x; // ✅ void* 可以指向 int
void* p2 = &y; // ✅ void* 可以指向 double
void* p3 = &c; // ✅ void* 可以指向 char
return 0;
}
💡 特点:
void*不能解引用(因为编译器不知道要读几个字节)void*不能做指针算术(因为不知道步长)
(2) 2.2 void* 的用途
用途一:写通用函数(后面会学 std::memcpy 就是用 void*)
#include <iostream>
#include <cstring>
int main() {
int arr1[5] = {1, 2, 3, 4, 5};
int arr2[5];
// std::memcpy 用 void* 接收任何类型的指针
std::memcpy(arr2, arr1, 5 * sizeof(int));
for (int i = 0; i < 5; i++) {
std::cout << arr2[i] << " ";
}
std::cout << std::endl;
return 0;
}
用途二:动态内存分配(后面会学 malloc 返回 void*)
(3) 2.3 void* 的转换
要用 void* 指向的值,必须转换成具体类型的指针。
#include <iostream>
int main() {
int x = 5;
void* p = &x; // void* 指向 int
// ❌ 错误:不能直接解引用 void*
// std::cout << *p << std::endl;
// ✅ 正确:先转换成 int*
int* intPtr = static_cast<int*>(p);
std::cout << *intPtr << std::endl; // 输出 5
return 0;
}
💡 提示: static_cast 是 C++ 的类型转换运算符(后面会学),比 C 语言的 (int*)p 更安全。
3. 多级指针(指针的指针)
(1) 3.1 什么是指针的指针?
指针的指针就是存指针地址的指针(套娃)。
#include <iostream>
int main() {
int x = 5;
int* p = &x; // p 指向 x
int** pp = &p; // pp 指向 p(指针的指针)
std::cout << "x = " << x << std::endl;
std::cout << "*p = " << *p << std::endl; // 解引用一次:得到 x 的值
std::cout << "**pp = " << **pp << std::endl; // 解引用两次:得到 x 的值
return 0;
}
(2) 3.2 多级指针的声明
| 声明 | 含义 |
|---|---|
int x; |
x 是 int |
int* p; |
p 是 int*(指向 int 的指针) |
int** pp; |
pp 是 int**(指向 int* 的指针) |
int*** ppp; |
ppp 是 int***(指向 int** 的指针) |
💡 读法: 从右往左读。
int* p;→pis a pointer tointint** pp;→ppis a pointer to pointer toint
(3) 3.3 多级指针的用途
用途一:动态二维数组(后面会学)
// 动态分配一个 3×4 的二维数组
int** matrix = new int*[3]; // matrix 是指针的指针
for (int i = 0; i < 3; i++) {
matrix[i] = new int[4];
}
用途二:函数需要修改指针(少见,了解即可)
#include <iostream>
void allocateInt(int** ptr) {
*ptr = new int(5); // 修改 *ptr(即修改原指针)
}
int main() {
int* p = nullptr;
allocateInt(&p); // 传指针的地址
std::cout << *p << std::endl; // 输出 5
delete p; // 释放内存
return 0;
}
4. 指针和 const 的组合
(1) 4.1 四种组合
| 声明 | 含义 | 能不能改指针 | 能不能改指向的值 |
|---|---|---|---|
int* p; |
普通指针 | ✅ 能 | ✅ 能 |
const int* p; |
指向 const int 的指针 | ✅ 能 | ❌ 不能 |
int* const p = &x; |
const 指针(必须初始化) | ❌ 不能 | ✅ 能 |
const int* const p = &x; |
指向 const 的 const 指针 | ❌ 不能 | ❌ 不能 |
💡 记忆技巧: 从右往左读。
const int* p;→pis a pointer toconst intint* const p = &x;→pis aconstpointer toint
(2) 4.2 示例:const int*(常用)
#include <iostream>
void printArray(const int* arr, int size) {
for (int i = 0; i < size; i++) {
std::cout << arr[i] << " ";
}
std::cout << std::endl;
// ❌ 不能修改:arr[i] = 0; // 编译报错
}
int main() {
int arr[5] = {1, 2, 3, 4, 5};
printArray(arr, 5);
return 0;
}
输出:
1 2 3 4 5
💡 重点: const int* arr 是最常用的函数参数类型——表示"我只读,不修改"。
5. 实战:用指针实现字符串复制
▶ 示例 1:手写 strcpy(难度⭐⭐)
#include <iostream>
void myStrcpy(char* dest, const char* src) {
while (*src != '\0') {
*dest = *src; // 复制字符
dest++; // 移动指针
src++;
}
*dest = '\0'; // 加结束符
}
int main() {
char src[] = "Hello";
char dest[20];
myStrcpy(dest, src);
std::cout << "src = " << src << std::endl;
std::cout << "dest = " << dest << std::endl;
return 0;
}
输出:
src = Hello
dest = Hello
💡 提示: 这个程序用到了指针算术和解引用,是理解指针的绝佳练习。
▶ 示例 3:用指针遍历数组(难度⭐)
#include <iostream>
int main() {
int arr[] = {10, 20, 30, 40, 50};
int* p = arr; // p 指向数组首元素
std::cout << "用指针遍历数组:" << std::endl;
for (int i = 0; i < 5; i++) {
std::cout << "*p = " << *p << std::endl;
p++; // 指针移动到下一个元素
}
// 指针相减计算元素个数
int* end = arr + 5;
std::cout << "元素个数:" << end - arr << std::endl;
return 0;
}
输出:
用指针遍历数组:
*p = 10
*p = 20
*p = 30
*p = 40
*p = 50
元素个数:5
❓ 常见问题
Q:为什么 void 不能解引用?* A:因为
void是没有大小的类型——编译器不知道要读 1 个字节、4个字节、还是8个字节。必须转换成具体类型的指针(如
int*、double*),编译器才知道步长。
Q:多级指针有什么用? A:实际项目中,
int最常见于: > 1. 动态二维数组(如new int*[3]) > 2. 函数需要修改指针本身(如void foo(int p)可以修改p指向哪里)超过两级的指针(
int***等)不推荐——太难理解了。
Q:const int 和 int const 一样吗? A:一样!以下两种写法等价: > > const int p; // 推荐 > int const p; // 等价,但不常用 > > > 但
int* const p不一样——这是const指针(不能改指向)。
📖 小节
- 指针可以比较大小(只有指向同一数组时才有意义)
void*是通用指针,不能解引用,不能做指针算术- 多级指针是指针的指针(
int**) - 指针 + const 有四种组合,最常用的是
const int*(只读) - 用指针实现字符串复制是理解指针的绝佳练习
📝 作业
-
基础题 (Difficulty ⭐): 声明一个
int变量x = 5,再声明一个int*指针p指向x,再声明一个int**指针pp指向p。输出x、*p、**pp,验证它们相等。 -
进阶题 (Difficulty ⭐⭐): 写一个函数
void swap(int** a, int** b),交换两个指针的指向。
输入:p1 指向 x,p2 指向 y
调用 swap(&p1, &p2)
输出:p1 指向 y,p2 指向 x
- 挑战题 (Difficulty ⭐⭐⭐):
用
void*和指针算术,手写一个myMemcpy函数: - 函数原型:
void myMemcpy(void* dest, const void* src, size_t n) - 功能:从
src复制n个字节到dest - 提示:按字节复制(用
char*做指针算术)
- 指针加减整数:按指向类型大小偏移
- 指针相减:得到元素个数差
- 指针比较:可用于判定先后顺序
- void* 指针:无类型,不能解引用和算术
- 函数指针:存储函数的入口地址
6. 🚀 下一步
学会了指针进阶,接下来我们学习 引用(Reference)(第25课)—— 指针的"安全版",C++ 的重要特性!