多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

14.高级指针

14.高级指针 一、先复习基础什么是指针指针 存放地址的变量 普通变量存的是数值int a 10;指针变量存的是另一个变量的地址int *p a;a取 a 的地址*p解引用拿到地址里面存的值也就是 a 的值p指针变量本身存的是地址二、一级指针最基础int a 10; int *p a; *p 20; // 修改a的值a变成20p→ 地址*p→ 地址对应的值三、二级指针指向指针的指针一级指针存普通变量地址二级指针存一级指针变量的地址。int a 10; int *p a; int **pp p;图解关系pp 里面存着 p 的地址*pp等价于 p存着 a 的地址**pp等价于*p等价于 a 的值记忆口诀** 几个就要解几次引用。*常见用途函数内部修改指针变量本身的值。补充示例void change(int **pp) { *pp NULL; // 修改外面p本身存储的地址 } int main() { int a 10; int *p a; change(p); return 0; }原理如果只传一级指针change(p)函数里面只能修改*pa 的值改不了 main 里 p 存储的地址。四、指针与数组1. 数组名本质数组名是数组首元素的地址int arr[5]{1,2,3,4,5};arr等价于arr[0]数组下标写法和指针写法完全等价arr[2] *(arr2)原理arr2从首地址向后跳过 2 个 int得到第三个元素地址*(arr2)解引用取出该地址的值。 通用公式arr[i]永远等价于*(arri)易错坑数组名不能被赋值arr p;错误。数组名是地址常量。补充数组名仅有两个例外此时代表整个数组数组名→arr取整个数组地址sizeof(数组名)→ 计算整个数组总字节数不是单个指针大小int arr[5]; printf(%zu, sizeof(arr)); // 5*420字节不是8字节指针加减规则指针 ± 整数不是单纯地址数字加减自动乘以指向类型的字节大小。 只有同一块连续内存的指针才可以相减结果是元素个数不同内存区域指针不能相减。2. 指针数组指针数组一个数组里面每个元素都是指针。 格式int *parr[5];parr 是数组有 5 个元素每个元素类型int*整型指针举例子int a1,b2,c3; int *parr[3]{a,b,c};parr[0]存 a 的地址parr[1]存 b 的地址。区分口诀谁离 [] 近谁就是数组int *parr[5];→[]离 parr 近 → parr 是数组指针数组3. 数组指针指向数组的指针数组指针一个指针专门指向一整个数组。 格式int (*p)[5];括号不能丢()优先级高于[]p 是指针它指向一个含有 5 个 int 元素的数组int arr[5]{1,2,3,4,5}; int (*p)[5]arr;arr取整个数组的地址注意arr和arr值上数值一样但类型完全不同 虽然打印出来地址数字相同但是arr1往后跳过 1 个 int4 字节arr1往后跳过完整一整个数组5 个 int20 字节。区分口诀int (*p)[5];→()离 p 近 → p 是指针数组指针4. 二维数组本质二维数组int arr[3][4];可以看成3 个一维数组每个一维数组里面有 4 个 int。 数组名 arr 是第一行一维数组的地址。数组指针遍历二维数组示范int arr[3][4]{1,2,3,4,5,6,7,8,9,10,11,12}; int (*p)[4] arr;p 指向第一行p1指向第二行。五、函数指针1. 概念函数也有地址函数名就是函数的入口地址。 函数指针用来存放函数地址的指针变量。声明格式返回值类型 (*指针名)(参数列表);例子void test(int x){ printf(%d,x); } //定义函数指针pf指向test void (*pf)(int) test; //调用两种写法等价 pf(10); (*pf)(10);易错不能丢掉括号void *pf(int);这不是函数指针这是声明一个叫 pf 的函数返回值是void*。补充用途除菜单、计算器外还用于回调函数把函数作为参数传给另一个函数。2. 函数指针数组存放多个函数地址的数组。void (*parr[3])(int);parr 是数组每个元素都是一个参数 int、返回 void 的函数指针。 经典用处菜单选项、简易计算器。六、字符指针 字符串易错点char *str hello world;hello world是字符串常量存放在只读区 千万不要str[0]x;修改常量字符串会程序崩溃。对比字符数组char str[] hello world;字符串放到数组里数组在栈区可以修改字符。七、const 修饰指针4 种情况const int *p;/int const *p;*p不能改p 本身可以改不能通过指针修改指向的值指针可以换指向别的变量int *const p;p 不能改*p可以改指针不能换目标指向的值可以修改const int *const p;p 和*p全都不能改双锁定记忆小技巧看 const 离谁近const 在*左边 → 修饰*p值不能改const 在*右边 → 修饰 p地址不能改八、void* 无类型指针万能指针void*可以接收任意类型变量的地址。 但是不能直接解引用不能加减偏移。使用前必须强制转换成对应类型指针。int a10; void *pa; //*p; 错误 int *q (int*)p; printf(%d,*q);用途写通用函数比如内存拷贝 memcpy。九、野指针野指针指针存了一个随机、无效的地址。 三大产生原因指针没有初始化局部指针变量默认随机垃圾值int *p;没有赋值 → 野指针指针指向的内存已经被释放函数局部变量出作用域销毁指针越界访问解决办法定义指针立刻初始化暂时不用就赋值 NULLint *pNULL;不要返回局部变量的地址保证指针访问范围合法补充区分空指针 ≠ 野指针NULL 是空指针地址为 0代表指针不指向任何有效空间解引用空指针程序直接崩溃。 空指针明确赋值为 0可以判断if(p NULL)野指针随机垃圾地址无法判断风险更大 NULL 本质宏定义#define NULL ((void*)0)十、汇总表格易混淆概念表格写法名称本质int *p;一级指针指针变量存 int 地址int **pp;二级指针指针存放一级指针变量的地址int *arr[5];指针数组数组每个元素是 int * 指针int (*p)[5];数组指针指针指向一个 5 元素 int 数组void (*pf)(int);函数指针指针存放函数入口地址指针选择题高频坑汇总arr[i]↔*(arri)i[arr]也是合法写法等价于*(iarr)偏题了解即可字符常量字符串char *pxxx只读char arr[]xxx在栈区可修改区分void (*pf)()函数指针 vsvoid *pf()返回 void * 的函数野指针没有办法检测只能提前预防void* 可以保存任意地址不能直接解引用、不能做加减运算
返回列表