【C 语言系统入门教程】第 12 讲:深入理解指针 (2) | 零基础学习笔记
【C 语言系统入门教程】第 12 讲深入理解指针 (2) | 零基础学习笔记前言上一讲我们学习了指针的基础概念、指针变量、指针运算这一讲继续深入指针的高级用法。本讲重点讲解const 修饰指针、野指针、assert 断言、传址调用这些是写出安全、规范、健壮指针代码的必备知识。系列往期笔记第 1 讲C 语言常见概念第 2 讲C 语言数据类型和变量第 3 讲分支和循环上第 4 讲分支和循环下第 5 讲数组第 6 讲函数第 7 讲数组和函数实践扫雷游戏第 8 讲VS 实用调试技巧第 10 讲操作符详解第 11 讲深入理解指针 (1) 本讲学习目标掌握const 修饰指针的四种情况分清 * 左边与右边的区别。理解野指针的成因学会规避野指针的 4 种方法。学会使用assert 断言增强代码安全性。彻底理解传值调用 vs 传址调用知道什么时候必须用指针。能用指针模拟实现strlen理解函数传址的核心思想。 核心学习内容1. const 修饰指针const 用来限制 “修改权限”让变量或指针指向的内容不能被随意修改。1.1 const 修饰普通变量const int n 0; n 20; // 错误不能修改1.2 const 修饰指针变量重点const 放在*左边 vs 右边意义完全不同。写法含义能否修改指向内容能否修改指针本身int *p普通指针能能const int *p指向内容不能改不能能int * const p指针本身不能改能不能const int * const p都不能改不能不能口诀const 在 * 左限制内容const 在 * 右限制指针。// 1. 普通指针 int* p; *p 10; // 可以 p a; // 可以 // 2. const 在 * 左边指向内容不能改 const int* p; *p 10; // 错误 p a; // 可以 // 3. const 在 * 右边指针本身不能改 int* const p; *p 10; // 可以 p a; // 错误 // 4. 两边都 const都不能改 const int* const p; *p 10; // 错误 p a; // 错误const 修饰指针示意图2. 野指针野指针指向非法 / 无效 / 未知内存地址的指针。使用野指针会导致程序崩溃、乱码、逻辑异常。2.1 野指针的 3 个成因指针未初始化int *p; *p 10; // 野指针2.指针越界访问int arr[10]; int *p arr; for(i0; i11; i) *(p) i; // 越界 → 野指针3.指针指向已释放的空间返回局部变量地址int* test() { int n 10; return n; // 局部变量出函数销毁返回野指针 }2.2 如何规避野指针4 个方法指针必须初始化不知道指向哪就赋值NULL小心指针越界不再使用的指针及时置为 NULL避免返回局部变量地址int *p NULL; // 安全初始化3. assert 断言assert是一个调试宏用来检查条件是否成立。不成立直接报错并停止程序帮助快速定位 bug。使用#include assert.h assert(p ! NULL);特点Debug 版本生效Release 版本会自动优化掉可以用#define NDEBUG关闭所有断言示例模拟 strlenint my_strlen(const char* str) { assert(str ! NULL); // 断言str不为空 int count 0; while(*str) { count; str; } return count; }4. 指针的使用与传址调用4.1 传值调用形参是实参的一份临时拷贝函数内部修改不影响实参交换函数用传值调用无效void Swap1(int x, int y) { int tmp x; x y; y tmp; } // 调用Swap1(a,b); → 交换失败4.2 传址调用把变量地址传给函数函数内部通过指针直接修改主函数变量交换函数必须用传址调用void Swap2(int* px, int* py) { int tmp *px; *px *py; *py tmp; } // 调用Swap2(a,b); → 交换成功结论只需读取数据 →传值调用需要修改主函数变量 →传址调用 课后习题一、选择题const int *p含义正确的是A. p 不能指向别处B. *p 不能改C. 都不能改D. 都能改下列哪个不是野指针的成因A. 指针未初始化B. 指针越界C. 指向 NULLD. 返回局部变量地址交换两个整型变量必须使用A. 传值调用B. 传址调用C. 递归D. 全局变量assert 宏需要包含哪个头文件A. stdio.hB. stdlib.hC. assert.hD. string.hint * const p中限制的是A. *pB. pC. 都限制D. 都不限制二、判断题const 在 * 左边限制指针指向的内容不能修改。野指针可以正常使用不会报错。传址调用可以在函数内部修改主函数变量。指针初始化为 NULL 可以避免野指针。传值调用中形参修改会影响实参。三、编程题用 const 改进上一讲的my_strlen并加入 assert。写一个交换两个 int 变量的函数必须用传址调用。写出 4 种 const 修饰指针的写法并注释各自权限。 参考答案一、选择题BCBCB二、判断题√×√√×三、编程题1. 安全版 my_strlen#include stdio.h #include assert.h int my_strlen(const char* str) { assert(str ! NULL); int count 0; while (*str ! \0) { count; str; } return count; } int main() { int len my_strlen(hello); printf(%d\n, len); return 0; }2. 传址调用交换函数#include stdio.h void Swap(int* x, int* y) { int tmp *x; *x *y; *y tmp; } int main() { int a 10; int b 20; Swap(a, b); printf(%d %d\n, a, b); return 0; }3. 四种 const 指针写法// 1. 普通指针 int* p1; // 2. 指向内容不能改 const int* p2; // 3. 指针本身不能改 int* const p3; // 4. 都不能改 const int* const p4; 本讲总结const 指针* 左限内容* 右限指针。野指针非常危险必须初始化、防越界、及时置 NULL。assert用于调试检查增强代码安全性。传值调用拷贝不影响实参。传址调用传地址可直接修改实参交换函数必用。指针的核心价值让函数与主函数真正互通数据。 版权说明本文为个人学习笔记整理配套课程课件内容未经允许禁止转载如有错误欢迎评论区指正