指针的定义、初始化和引用
从交换数据的需求出发,彻底搞懂指针的定义、初始化与引用
按 空格/→ 演示下一步
全部页面点击任意一页,跳回舞台从这页播放
指针的定义、初始化和引用
从交换数据的需求出发,彻底搞懂指针的定义、初始化与引用
变量的地址:内存的门牌号
上页说指针是「装地址的盒子」——那地址本身是什么?去酒店前台领房卡,工作人员不会说「花园景观房」,而是直接报一个号码:1208。这个号码就是地址。
整栋楼=内存;房间号=地址;前台报号=&;拿号入住=指针存地址
地址的获取与查看
用 & 取地址、%p 打印,亲眼看到地址就是一个可运算的数值。
& 取地址,%p 十六进制展示;强转后地址就是整数;地址相减得字节距离——地址可参与算术。
指针变量的定义语法
上一页我们用 `&` 拿到了变量的地址,但每次只是'看一眼'。要把地址存下来反复用,就得用指针变量——这一页看它的定义语法。
指向 int 的尺每格 4 字节,指向 double 的每格 8 字节——指针 +1 跳多少字节,由类型决定
指针变量的内存模型
对比看:普通变量存值,指针变量存地址,地址再指向那个值。
指针的初始化:指向已有变量
完整演示从变量定义、地址获取、指针初始化到解引用的全过程,并验证两者绑定关系。
L4 定义变量;L5 是指针初始化的正确写法(&a 取出地址);L8~L9 验证 p 与 &a 是同一地址;L10 通过 *p 读出 a 的值;L12 演示通过 *p 改写 a。
空指针 vs 野指针
空指针和野指针都叫'指针不能用',但一个可控一个失控,差别巨大。
- 明确赋值为 NULL,不指向任何对象
- 定义时初始化为 NULL,或暂时不用时置 NULL
- 解引用会触发段错误,程序崩溃
- 使用前判断 if(p!=NULL),用完置 NULL
- 未初始化,或指向已释放/越界的地址
- 局部变量忘赋初值,free 后未置 NULL
- 解引用行为未定义,可能崩溃也可能读到垃圾
- 定义时立即初始化,free 后立即置 NULL
解引用运算符 * 的含义
上一页我们把地址塞进了指针变量 p。但只拿到地址还不够——我们要的是地址背后那个真正的值。怎么从地址取出值?这就轮到解引用运算符 * 出场了。
取件码告诉你柜门位置(地址 p);扫码开柜取出包裹才是 *p。只看码不扫码,永远拿不到里面的东西
解引用的读写操作
用指针 *p 完成一次读、一次写,直观看清解引用的两种角色。
L7 中 *p 出现在 = 右侧做读取,L8 中 *p 出现在 = 左侧做写入;输出印证了对同一块内存的两种操作。
指针读写的内存流程图
纵向看五阶段:先建变量与指针,再用赋值把指针指向变量,最后演示 *p 读写对变量值的双向影响。
指针操作自测
执行下面代码后,变量 a 的最终值是? int a=5, b=8; int *p=&a; p=&b; *p=20;
值传递的局限:为何 swap 失败
前面我们拿到了变量的地址,也学会了用 * 读写真实内存。可写一个 swap(a, b) 交换两个整数,运行后 a、b 却纹丝不动——问题出在哪?
在复印件上涂改画叉,原件不会有任何变化;形参就是那张复印件
错误的 swap 函数
用一段最经典的 C 代码演示——swap(a,b) 调用后,a 和 b 居然纹丝不动。问题出在哪?
x、y 是形参,函数内交换的只是 a、b 的副本;原变量 a、b 在 main 里纹丝不动。这就是值传递——把值的拷贝交出去。
指针传递:让函数看到实参地址
上次 swap(a,b) 失败是因为函数只拿到值的副本,根本碰不到原变量。这次换个思路:把 a、b 的地址交给函数,让它顺着地址直接修改原变量。
上门能直接搬动原家具;只口头报布局,师傅只能在他自己家照样摆
正确的 swap 函数实现
形参换成指针后,函数内部就能直接改写实参本体。
形参改为 int *p1, int *p2,调用时传入 &a, &b,函数内通过 *p1、*p2 的读写直接修改 a、b 本体所在的内存单元。
指针传递交换数据的完整流程
从左到右、从上到下读:main 发起调用,swap 通过形参指针直接操作实参所在内存,最终实参值互换。
传值 vs 传指针:调用栈对比
两种参数传递方式在调用栈上走了完全不同的路,swap 的成败正是分水岭。
- 压栈:复制实参的值到新栈帧
- 形参与实参地址不同,是两份数据
- 改形参不影响实参,swap 因此失败
- 函数返回,栈帧弹出,形参消失
- 压栈:复制的是地址(一个整数)
- 形参是指针,但指向同一块实参
- 用 * 解引用改的就是实参本体
- 返回后指针销毁,实参数据仍保留
指针参数应用题
希望 swap(a, b) 能真正交换调用者两个 int 变量的内容(而非副本),下列哪个实现正确?
野指针的成因与危害
两个会真实触发的野指针崩溃场景:未初始化指针与悬空指针
L10 是未初始化的「种子」,L11 触发崩溃;L14 接过已死地址,L15 读取悬空——野指针的两种成因都指向未定义行为。
安全使用指针的 checklist
野指针像没系安全带的司机——撞上去才发现没保护。光知道有雷不够,每次用指针前过一遍清单,才能把隐患堵在门外。
绕车一周看胎压/灯光/油量,对应初始化/判空/不越界三项逐条过
本周知识要点回顾
- ✓指针本质是存储地址的变量,类型决定解读方式
- ✓解引用就是按地址找到内存并读写数据
- ✓传指针让函数能修改实参,打破值传递局限
- ✓指针使用前必须初始化,警惕空指针和野指针
课后思考
先合上课件独立想一想,别急着翻参考答案。
参考答案指针类型决定了 +1 跳多少字节、解引用读几个字节。int* 步长 4、char* 步长 1,跨类型访问会读到错位的内存。
参考答案类型不匹配会触发编译警告或错误;若强转通过,函数会把 int 当地址解引用,立刻段错误或读到任意内存。
参考答案例外是 &arr 和 sizeof(arr)。前者取整个数组的地址(步长为整个数组),后者返回整个数组字节数而非指针大小。