GESP 6级
指针基础
6级 · 指针/STL/复杂度
💡 为什么重要
指针是C++最强大也最难理解的特性之一。在GESP 6级考试中,指针几乎是必考内容。更关键的是,后面你要学的动态内存(new/delete)、链表、树等数据结构,全部依赖指针来实现。可以说,指针是通往高级C++的"钥匙"。如果你现在觉得难,不用担心——我们一步一步来。
📖 前置知识
在学习指针之前,你需要掌握以下内容(这些是GESP 1-4级的知识):
- 变量的声明和赋值(如
int a = 5;) - 基本数据类型(int, double, char等)
- cout 和 cin 的基本用法
- 数组的基本用法(如
int arr[5] = {1,2,3,4,5};) - 函数的基本定义和调用
🔍 什么是"地址"和"指针"?
想象一栋大楼,每个房间都有一个门牌号(地址)。电脑内存也是一样的:每个变量住在内存里的某个"房间",每个房间有一个唯一的编号(地址)。
指针就是一个存着"门牌号"的变量。它不直接装数据,而是装"数据住在哪"这个地址。
打个比方:
int a = 10;→ a 是一个房间,里面住着数字 10int *p = &a;→ p 是另一个房间,里面住着 a 的门牌号(地址)
📐 核心语法一览
int a = 10; // 声明一个整型变量a,值为10
int *p = &a; // 声明一个指针p,指向a(&取地址符)
cout << *p; // 输出10(*解引用符:通过地址找内容)
*p = 20; // 通过指针修改a的值,a变成20
关键符号:
&(取地址符):获取变量的内存地址
*(解引用符):通过地址访问对应的值
*(声明时):表示这是一个指针变量
int *p = &a; // 声明一个指针p,指向a(&取地址符)
cout << *p; // 输出10(*解引用符:通过地址找内容)
*p = 20; // 通过指针修改a的值,a变成20
关键符号:
&(取地址符):获取变量的内存地址
*(解引用符):通过地址访问对应的值
*(声明时):表示这是一个指针变量
💻 代码详解
📝 示例1:指针的基本使用
#include <iostream>
using namespace std;
int main() {
int a = 5; // 声明变量a,值为5
// 假设a的内存地址是 0x1000
int *p = &a; // 声明指针p,存的是a的地址(0x1000)
// &a 表示"取a的地址"
// 此时 p 的值 = 0x1000
cout << p << endl; // 输出a的地址(类似 0x7ffd5c3a)
cout << *p << endl; // 输出5
// *p 表示"通过p存的地址,找到里面的值"
// 即通过 0x1000 找到 a,取出5
*p = 20; // 通过指针修改a的值
// *p = 20 等价于 a = 20
cout << a << endl; // 输出20(a的值已经被改了)
cout << *p << endl; // 输出20
return 0;
}
📝 示例2:指针与数组
#include <iostream>
using namespace std;
int main() {
int arr[] = {10, 20, 30, 40, 50};
// arr 本身就是首元素的地址,等价于 &arr[0]
int *p = arr; // p 指向 arr[0],即 p 存的是 arr[0] 的地址
// 也可以写成:int *p = &arr[0];
cout << *p << endl; // 输出10(arr[0]的值)
cout << *(p + 1) << endl; // 输出20(arr[1]的值)
cout << *(p + 2) << endl; // 输出30(arr[2]的值)
// ★ 关键点:p+1 不是地址+1,而是地址+sizeof(int)
// 因为int占4字节,所以p+1跳过4个字节,刚好到下一个int
// 这叫做"指针算术"
// 等价写法:
cout << p[0] << endl; // 输出10,指针也可以用下标!
cout << p[1] << endl; // 输出20
cout << p[2] << endl; // 输出30
// 用指针遍历数组
for (int *q = arr; q != arr + 5; q++) {
cout << *q << " "; // 依次输出 10 20 30 40 50
}
cout << endl;
return 0;
}
📝 示例3:指针作为函数参数
#include <iostream>
using namespace std;
// 普通传参:函数拿到的是值的副本,改不了原变量
void badSwap(int a, int b) {
int t = a;
a = b; // 只改了副本,外面的变量没变
b = t;
}
// 指针传参:函数拿到的是原变量的地址,可以直接改
void goodSwap(int *a, int *b) {
int t = *a; // 通过a的地址取出值
*a = *b; // 通过b的地址把值写到a指向的地方
*b = t; // 通过a的地址把旧值写到b指向的地方
}
int main() {
int x = 1, y = 2;
badSwap(x, y);
cout << x << " " << y << endl; // 输出 1 2(没变!)
goodSwap(&x, &y); // 传入x和y的地址
cout << x << " " << y << endl; // 输出 2 1(成功交换!)
return 0;
}
⚠️ 易错点
- 忘记初始化指针就使用:声明
int *p;后不赋值,p 指向随机位置(野指针),使用它会导致程序崩溃。一定要在声明时就赋值,如int *p = &a;或int *p = nullptr; - 混淆 * 和 & 的含义:* 在声明时表示"这是指针",在使用时表示"取指针指向的值";& 在使用时表示"取地址"
- 指针+1 的误解:
p+1不是把地址加1,而是跳过一个元素的大小。int*+1跳4字节,double*+1跳8字节 - 越界访问:指针指向数组后,不能访问超出数组范围的元素,否则会读到垃圾数据甚至崩溃
- 声明多个指针时的陷阱:
int *a, b;只有 a 是指针,b 是普通 int!应该写int *a, *b;
🎯 练习建议
- 入门:写一个程序,声明一个 int 变量,用指针读取并打印它的值
- 进阶:写一个用指针实现的 swap 函数,交换两个变量的值
- 挑战:写一个函数,用指针遍历数组并求和,不使用下标
- 调试练习:故意写错代码(如不初始化指针),观察运行结果,培养排错能力
- 画内存示意图:在纸上画出变量、地址、指针的关系,帮助理解