CC++ & Algorithm

指针基础

较难15
语言版本:C++
概述:指针就像门牌号,它告诉你一个变量住在内存的哪个地址,你可以通过门牌号找到它并修改它。

指针基础:用门牌号和遥控器理解内存地址

什么是指针?

想象一下,你有一排带编号的储物柜,每个柜子里可以放东西。C++程序运行时,变量也会被存放在这样的“柜子”里——我们叫它内存。每个内存单元都有一个独一无二的地址(就像门牌号)。指针就是一种特殊的变量,它不直接存放数据,而是存放另一个变量的地址,相当于“记下门牌号的小纸条”。

通过指针,你可以找到那个变量,查看它的值,甚至可以修改它——就像拿着朋友的住址纸条,找到他家,帮他拿走或放回东西。

代码初体验

#include <iostream>
using namespace std;

int main() {
    int age = 10;                  // 一个普通变量,年龄为10
    int* p = &age;                 // p是一个指针,保存了age的地址(纸条上写了门牌号)
    
    cout << "age 的值:" << age << endl;          // 直接看柜子里的东西:10
    cout << "age 的地址:" << &age << endl;       // 打印门牌号,比如0x7fff...
    cout << "指针 p 的值:" << p << endl;         // 纸条上写的地址:同上
    cout << "通过指针 p 访问 age:" << *p << endl; // *p 就是“按照纸条找到柜子”,看到里面的10
    
    // 通过指针修改变量
    *p = 20;                         // 通过纸条找到柜子,把里面的数改成20
    cout << "修改后 age:" << age << endl;        // 20,因为age和*p指向同一个柜子
    
    return 0;
}

指针的声明与初始化

声明一个指针

声明指针的语法:类型* 指针名;。比如:

int* ptr;       // 声明了一个可以存放int类型变量地址的指针
double* p_d;    // 只能存放double类型变量的地址
char* p_char;   // 存放字符变量的地址

生活中的类比:你准备一张空白纸条(指针),上面将来要写下地址。纸条的类型决定了你可以写哪种“门牌号”:比如“int型房间”的纸条只能记int型房间的门牌号。

初始化指针

指针必须初始化后才能使用,就像纸条上必须有地址才能找到朋友家。用&运算符获取变量的地址:

int score = 95;          // 考试分数
int* p_score = &score;   // p_score记下score的门牌号

double price = 3.5;      // 零食价格
double* p_price = &price; // p_price记下price的地址

注意int*类型的指针只能存放int变量的地址,double*只能存放double变量的地址,类型必须匹配,否则编译器会报错或产生诡异行为。

解引用:通过指针访问变量

解引用就是“按地址找到那个房间并操作里面的东西”。用*号放在指针前面,例如*p就代表p指向的那个变量。

读数据

int money = 50;          // 零花钱
int* p_money = &money;
cout << *p_money << endl; // 输出50,等价于 cout << money;

写数据

*p_money = 100;          // 把零花钱改成100,等价于 money = 100;
cout << money << endl;   // 输出100

生活例子:遥控器和电视

  • 电视是变量,遥控器是指针。
  • 遥控器上写着“电视1的地址”(指针的值是地址)。
  • 按下遥控器的“+”键(解引用操作*),电视音量就增加。
  • 遥控器本身只存储了一个编号,真正改变的是电视。

你甚至可以换一台电视:把遥控器的频道指向另一个电视(改变指针的指向)。但同一个时刻只能控制一台。

指针就像地址纸条:更多形象比喻

  • 队列游戏:你手里有一张纸条,上面写着“排在第三个的同学的名字”。这张纸条就是指针,而那个同学就是变量。通过纸条你叫出那个同学,让他往前一步(修改他的位置)。
  • 修改作业:你和同桌交换了练习册。你有一张纸条写着“我的练习册在老师办公室桌子的右边格子里”。指针就是纸条,通过纸条找到练习册,把答案改对。

新手容易犯的错误

1. 空指针与野指针

未初始化的指针(也叫野指针):

int* p;          // 没给地址,p指向一个随机内存位置
*p = 10;         // 危险!可能把数据写到了不该写的地方,程序崩溃

就像你拿着一张空白的没有地址的纸条,却想象自己能通过它找到朋友家——这不可能,还会迷路。

空指针:指针指向地址0,表示它不指向任何有效变量。

int* p = nullptr;  // C++11推荐,或者写成 NULL / 0
if (p != nullptr) {
    *p = 5;        // 安全:只有非空才解引用
}

2. 类型不匹配

int score = 85;
double* p_d = &score;   // 错误!int* 不能赋给 double*
// 或者
int* p = &score;        // 正确
double d = 3.14;
p = &d;                 // 错误!double* 不能赋给 int*

类型不匹配就像你拿着“水果屋”的地址纸条,却想进“蔬菜屋”的门,系统不允许。

3. 忘记解引用

int a = 10;
int* p = &a;
cout << p << endl;      // 输出的是地址,不是a的值
cout << *p << endl;     // 正确输出a的值
int b = 20;
*p = 30;                // 正确:把b改成30
p = 30;                 // 错误!p是指针,不能直接赋整数(除非类型转换)

完整示例:用指针交换两个数

交换两个整数是初学指针时很经典的应用,就像交换两个同学的座位。

#include <iostream>
using namespace std;

int main() {
    int candy_a = 5;       // 小明有5颗糖
    int candy_b = 8;       // 小红有8颗糖
    
    cout << "交换前:小明=" << candy_a << ",小红=" << candy_b << endl;
    
    // 用指针交换两个变量的值
    int* p_a = &candy_a;   // p_a指向小明的糖数
    int* p_b = &candy_b;   // p_b指向小红的糖数
    
    int temp_candy = *p_a; // 先把小明的糖数暂存到临时变量
    *p_a = *p_b;           // 小明的糖数变成小红的糖数
    *p_b = temp_candy;     // 小红的糖数变成原来的小明的糖数
    
    cout << "交换后:小明=" << candy_a << ",小红=" << candy_b << endl;
    
    // 也可以不用指针,直接用变量交换:
    // int temp = candy_a;
    // candy_a = candy_b;
    // candy_b = temp;
    // 上面那种方法更直观,但指针版本展示了如何通过地址操控变量
    
    return 0;
}

运行结果:

交换前:小明=5,小红=8
交换后:小明=8,小红=5

相关指引

学会了指针的基础,下一步可以学习:

  • 结构体指针:用指针操作结构体变量(比如学生信息 struct)。
  • 动态内存分配:new 和 delete 创建和释放内存,这是程序运行时临时申请“抽屉”的方法。
  • 函数指针:把函数当作参数传递,或者保存函数的地址。
  • 指针与数组:数组名其实就是一个常量指针,操作数组时指针非常高效。

指针是C++中非常强大的工具,但也要小心使用,就像玩火一样,用好了能做饭,用不好会烧到手指。多写代码、多调试,就能逐渐掌握它。

例题精讲

1单选题

若有定义 int a=10, *p=&a; 则表达式 *p 的值是?

A变量a的地址
B10
C变量p的地址
D不确定
2单选题

以下关于空指针的说法正确的是?

A空指针指向地址0的变量
B对空指针解引用会导致程序崩溃
Cint *p=nullptr; 等价于 *p=0;
D空指针可以参与算术运算
3判断题

指针变量中存储的是它所指向变量的地址。

4填空题
有结构体定义及代码:
struct Student {
    int id;
    string name;
};
Student s;
Student *p = &s;
要通过指针p给s的id成员赋值为101,请填空:
p->id = 101;   // 或 (*p).id = 101;
此处填写最简洁的形式:___ = 101;
5填空题
设 int arr[5] = {10,20,30,40,50};
int *p = arr;
要使p指向数组中的第三个元素(值为30),请填空:
p = ___;