指针基础
较难15指针基础:用门牌号和遥控器理解内存地址
什么是指针?
想象一下,你有一排带编号的储物柜,每个柜子里可以放东西。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++中非常强大的工具,但也要小心使用,就像玩火一样,用好了能做饭,用不好会烧到手指。多写代码、多调试,就能逐渐掌握它。
例题精讲
若有定义 int a=10, *p=&a; 则表达式 *p 的值是?
以下关于空指针的说法正确的是?
指针变量中存储的是它所指向变量的地址。
有结构体定义及代码:
struct Student {
int id;
string name;
};
Student s;
Student *p = &s;
要通过指针p给s的id成员赋值为101,请填空:
p->id = 101; // 或 (*p).id = 101;
此处填写最简洁的形式:___ = 101;设 int arr[5] = {10,20,30,40,50};
int *p = arr;
要使p指向数组中的第三个元素(值为30),请填空:
p = ___;