1 指针基础
正在加载视频播放器…
GESP C++ 四级:指针基础精讲
核心目标:理解指针的概念与内存模型,掌握指针变量的定义、赋值、解引用,掌握指针与数组的关系,掌握指针作为函数参数的传递方式,了解const与指针、指针运算、空指针与野指针的安全用法。
指针就是“存放地址的变量”;&取地址,*解引用;指针的类型决定它“看多远”,指针的运算决定它“走多远”;指针传参让函数可以修改外面的变量。
一、考纲定位
根据CCF官方考纲,四级的知识目标明确要求:
- 理解C++指针类型的概念,掌握指针类型变量的定义、赋值、解引用。
- 掌握函数参数的传递方式:C++值传递、引用传递、指针传递。
- 掌握C++二维及多维数组的基本概念及使用。
四级考纲中指针相关内容主要包括:指针类型的概念、指针变量的定义与引用、字符型指针变量、指针变量作函数参数。指针是四级考试的分水岭,理论理解与实际解题之间往往存在落差,需要重点突破。
二、指针的基本概念
1. 什么是内存地址
计算机的内存可以看作一排连续的“小格子”,每个格子有一个编号,这个编号就是内存地址。变量存储在内存中,也有自己的地址。
int a = 10;
cout << &a << endl; // 输出变量a的内存地址,如 0x7ffee4065820
& 是取地址运算符,用于获取变量的内存地址。
2. 指针是什么
指针是一种特殊的变量,它存储的不是普通的数据值,而是另一个变量的内存地址。
int a = 10;
int *p = &a; // p是一个指针变量,存储了a的地址
此时,p 的值是 a 的地址,我们说“p指向a”。
3. 指针的核心操作符
| 操作符 | 名称 | 作用 | 示例 |
|---|---|---|---|
& |
取地址运算符 | 获取变量的内存地址 | &a 得到 a 的地址 |
* |
解引用运算符 | 获取指针指向的值 | *p 得到 p 指向的值 |
记忆口诀:& 是“找地址”,* 是“看内容”。
三、指针变量的定义与初始化
1. 定义语法
数据类型 *指针变量名;
int *p; // 指向int类型的指针
double *q; // 指向double类型的指针
char *c; // 指向char类型的指针
关键规则:指针变量的类型必须与它所指向的变量类型一致。
int a = 10;
int *p = &a; // ✅ 正确:p指向int
double *q = &a; // ❌ 错误:类型不匹配
2. 初始化方式
int a = 10;
// 方式一:定义时直接初始化
int *p = &a;
// 方式二:先定义,后赋值
int *q;
q = &a;
// 方式三:空指针(安全初始化)
int *r = nullptr; // C++11推荐写法
int *s = NULL; // C风格写法
3. 指针的大小
指针变量的大小与所指向的数据类型无关,只取决于编译环境:
| 系统 | 指针大小 |
|---|---|
| 32位系统 | 4字节 |
| 64位系统 | 8字节 |
cout << sizeof(int*) << endl; // 8(64位系统)
cout << sizeof(char*) << endl; // 8(64位系统)
cout << sizeof(double*) << endl; // 8(64位系统)
真题示例:在C++中,指针变量的大小(单位:字节)是( )。答案:与编译器有关。
四、解引用
1. 基本用法
解引用就是通过指针访问它指向的内存空间。
int a = 10;
int *p = &a;
cout << *p << endl; // 输出 10(读取a的值)
*p = 20; // 修改a的值为20
cout << a << endl; // 输出 20
2. 解引用的本质
*p 就等价于 a 本身。对 *p 的操作,就是对 a 的操作。
int a = 5;
int *p = &a;
*p += 7; // 等价于 a += 7
cout << a; // 输出 12
3. 二级指针(了解)
二级指针是指向指针的指针:
int a = 5;
int *p = &a;
int **q = &p; // q是指向p的指针
**q += 7; // 等价于 a += 7
cout << a; // 输出 12
五、指针与数组
1. 数组名的本质
数组名就是数组第一个元素的地址(首地址)。
int arr[5] = {10, 20, 30, 40, 50};
int *p = arr; // 等价于 int *p = &arr[0]
cout << *p << endl; // 输出 10
2. 通过指针访问数组元素
int arr[5] = {10, 20, 30, 40, 50};
int *p = arr;
cout << *p << endl; // 10(arr[0])
cout << *(p + 1) << endl; // 20(arr[1])
cout << *(p + 2) << endl; // 30(arr[2])
*(p + i) 等价于 arr[i],也等价于 p[i]。
3. 指针运算
指针的加减运算以所指向类型的大小为单位:
int arr[3] = {24, 9, 7};
int *p = arr;
p++; // p向后移动 sizeof(int) 字节,指向 arr[1]
cout << *p; // 输出 9
真题示例:假设整数数组
arr[4] = {0, 1, 2, 3};的第一个元素在内存中的地址为0x7ffee4065820,经过int* p = arr; p += 1;后,指针p的值是( )。答案:0x7ffee4065824(假设 int 占4字节)。
六、指针作为函数参数
1. 三种参数传递方式对比
| 传递方式 | 语法 | 函数内修改是否影响实参 | 典型用途 |
|---|---|---|---|
| 值传递 | void f(int x) |
❌ 不影响 | 只读参数 |
| 引用传递 | void f(int &x) |
✅ 影响 | 需要修改实参 |
| 指针传递 | void f(int *x) |
✅ 影响(需解引用) | 需要修改实参、可传空指针 |
2. 指针传递示例
void change(int *p) {
*p = 100; // 通过解引用修改指向的值
}
int main() {
int n = 7;
change(&n); // 传入n的地址
cout << n; // 输出 100
return 0;
}
3. 真题示例
执行下面程序后,输出为( )。
void fun(int a, int &b, int *c){ a += 1; b += 2; *c += 3; } int main(){ int x = 1, y = 1, z = 1; fun(x, y, &z); cout << x << " " << y << " " << z; }答案:1 3 4
解析:a为值传递,修改不影响x;b为引用传递,修改影响y;c为指针传递,*c += 3修改了z。
4. 交换两个变量的值
void xchg(int *a, int *b) {
int t = *a;
*a = *b;
*b = t;
}
int main() {
int x = 10, y = 20;
xchg(&x, &y);
cout << x << " " << y; // 输出 20 10
return 0;
}
七、const与指针
1. 三种const指针写法
| 写法 | 含义 | 能否修改指向的值 | 能否修改指向 |
|---|---|---|---|
const int *p |
指向常量的指针 | ❌ | ✅ |
int * const p |
常量指针 | ✅ | ❌ |
const int * const p |
指向常量的常量指针 | ❌ |
记忆口诀:const 靠近谁,谁就不能改。
2. 常见用途
void print(const int *p) {
// *p = 100; // ❌ 错误:不能修改const指针指向的值
cout << *p; // ✅ 可以读取
}
用 const 修饰指针参数,表明函数不会修改传入的数据,是良好的编程习惯。
八、空指针与野指针
1. 空指针
int *p = nullptr; // 安全初始化
if (p != nullptr) {
cout << *p; // 先判断再解引用
}
空指针不指向任何有效内存,解引用空指针会导致程序崩溃。
2. 野指针
未初始化的指针指向随机地址,称为野指针。
int *p; // ❌ 野指针:未初始化
cout << *p; // 危险!访问随机内存
安全规则:指针定义时要么初始化为 nullptr,要么初始化为有效地址。
九、易错点
- 指针类型与指向类型不匹配:
int *p = &a;中a必须是int类型。 - 忘记解引用:
*p = 20修改指向的值,p = &b修改指向的地址,两者不同。 - 解引用未初始化的指针:野指针解引用会导致未定义行为。
- 混淆
*在定义和表达式中的含义:int *p;中*是声明符,*p = 10;中*是解引用运算符。 - 指针运算单位错误:
p + 1跳过的是sizeof(类型)字节,不是1字节。 - 数组名不能重新赋值:
arr = p;是错误的,数组名是常量指针。 - 空指针解引用:使用前必须判断
p != nullptr。 - 函数内修改指针本身 vs 修改指针指向的值:
p = &b只改变函数内的局部指针,不影响外面的指针;*p = 20才会影响外面的变量。
十、阅读程序写结果
题目 1
int a = 10;
int *p = &a;
*p = 20;
cout << a << endl;
输出:20
题目 2
int arr[3] = {24, 9, 7};
int *p = arr;
p++;
cout << *p << endl;
输出:9
题目 3
int x = 20;
int y = 24;
int *p = &x;
int *q = &y;
p = q;
cout << *p << endl;
输出:24(p 改为指向 y)
题目 4
int a = 5;
int *p = &a;
int **q = &p;
**q += 7;
cout << a << " " << *p;
输出:12 12
十一、速查表
| 需求 | 写法 | 说明 |
|---|---|---|
| 取地址 | &a |
获取变量a的地址 |
| 定义指针 | int *p; |
指向int的指针 |
| 初始化指针 | int *p = &a; |
p指向a |
| 解引用读取 | *p |
读取p指向的值 |
| 解引用修改 | *p = 20; |
修改p指向的值 |
| 空指针 | int *p = nullptr; |
安全初始化 |
| 数组首地址 | int *p = arr; |
等价于 &arr[0] |
| 指针算术 | p + 1 |
移动一个元素的大小 |
| 指针传参 | void f(int *p) |
函数可修改实参 |
| 交换变量 | void xchg(int *a, int *b) |
指针版交换 |
| const指针 | const int *p |
不能修改指向的值 |
| 二级指针 | int **q = &p; |
指向指针的指针 |
十二、总结
指针是GESP四级最核心也最具挑战性的知识点。
- 指针是存放内存地址的变量,
&取地址,*解引用。 - 指针类型必须与所指向的变量类型一致。
- 指针大小与编译环境有关(32位4字节,64位8字节),与指向类型无关。
- 数组名就是首地址,
*(p + i)等价于arr[i]。 - 指针运算以所指向类型的大小为单位。
- 指针传参可以让函数修改外面的变量,值传递不行。
- 引用传递和指针传递都能修改实参,但指针可以传空值,引用必须绑定有效变量。
const int *p不能改指向的值,int * const p不能改指向。- 指针使用前必须初始化(
nullptr或有效地址),杜绝野指针。 - 多练“解引用写结果”“指针传参判断”“指针与数组结合”三类题。
复习建议:先理解内存模型和取地址/解引用的基本操作,再练习指针与数组的结合,然后重点突破指针作为函数参数的传参判断(值传递、引用传递、指针传递三者的区分)。考试中先判断指针指向哪里,再看函数参数是哪种传递方式,最后逐行跟踪变量的变化。