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,要么初始化为有效地址。

九、易错点

  1. 指针类型与指向类型不匹配:int *p = &a; 中 a 必须是 int 类型。
  2. 忘记解引用:*p = 20 修改指向的值,p = &b 修改指向的地址,两者不同。
  3. 解引用未初始化的指针:野指针解引用会导致未定义行为。
  4. 混淆 * 在定义和表达式中的含义:int *p; 中 * 是声明符,*p = 10; 中 * 是解引用运算符。
  5. 指针运算单位错误:p + 1 跳过的是 sizeof(类型) 字节,不是1字节。
  6. 数组名不能重新赋值:arr = p; 是错误的,数组名是常量指针。
  7. 空指针解引用:使用前必须判断 p != nullptr。
  8. 函数内修改指针本身 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四级最核心也最具挑战性的知识点。

  1. 指针是存放内存地址的变量,&取地址,*解引用。
  2. 指针类型必须与所指向的变量类型一致。
  3. 指针大小与编译环境有关(32位4字节,64位8字节),与指向类型无关。
  4. 数组名就是首地址,*(p + i) 等价于 arr[i]。
  5. 指针运算以所指向类型的大小为单位。
  6. 指针传参可以让函数修改外面的变量,值传递不行。
  7. 引用传递和指针传递都能修改实参,但指针可以传空值,引用必须绑定有效变量。
  8. const int *p 不能改指向的值,int * const p 不能改指向。
  9. 指针使用前必须初始化(nullptr 或有效地址),杜绝野指针。
  10. 多练“解引用写结果”“指针传参判断”“指针与数组结合”三类题。

复习建议:先理解内存模型和取地址/解引用的基本操作,再练习指针与数组的结合,然后重点突破指针作为函数参数的传参判断(值传递、引用传递、指针传递三者的区分)。考试中先判断指针指向哪里,再看函数参数是哪种传递方式,最后逐行跟踪变量的变化。