一.指针的概念本质上指针是一个变量他的值不是数据而是另一个变量在内存的地址。*解引用运算符取地址运算符-结构体/联合体指针成员访问符;[ ]:下标运算符.:直接成员访问符二.指针的算术运算指针加法对指针进行加法运算时指针会移动到它所指类型的下一个元素。int arr[5]{1,2,3,4,5}; int *parr; p;\\p指向arr[1];即arr[1]减法同理只不过时向前移动sizeofint个字节。三.指针常量和常量指针常量指针指针常量语法const int*p或int const* pint *const p指向的值不能通过*p修改可以修改指针的指向可以指向其他变量初始化后不能指向其他地址典型用途防止函数修改数据固定指针指向的地址四.为什么函数参数常用const修饰1.防止意外修改const 可以保证参数在函数内部不会被修改。特别是对于指针参数能避免了意外改变原始数据。2.明确函数意图看到 const 参数能立刻明白这个参数是只读的。这降低了沟通和理解的成本。3.允许传入常量或字面量如果参数没有 const 修饰函数就可能被设计成“可修改”的导致无法安全地传入字符串字面量或 const 变量。4.编译器可做优化const 向编译器提供了额外的信息不必担心内存被更改从而生成更高效的代码。常量指针常量const int *const p既不会修改常量也不会改变指针指向的地址。指针数组和数组指针指针数组本质是数组数组里的元素均为指针数组指针本质是指针指针指向一个数组。指针数组数组指针声明语法类型 *数组名[长度]类型*指针名[长度]内存布局连续存储多个指针变量只存储一个地址指向某数组的首地址占用空间sizeof指针大小*元素数量sizeof指针本身的大小常见用途存储多个字符串处理多行文本指向二维数组的行在函数见传递多维数组元素访问通过下标访问指针arr[i]得到指针*arr[i]得到h值先解引用得到数组再下标*ptr[i]或ptr[0][i]与二维数组的关系动态的“锯齿状”二维数组指向固定的二维数组五.1.数组名退化大多数情况下数组名会被转换为其首元素的指针。退化的类型结果指向元素类型的指针。退化后的值数组首地址元素的地址即arr[0]。2.什么场景会发生退化1.作为函数参数赋值给指针参与算术运算用*解引用用[]下标。3.不退化的特殊场景有哪些sizeof数组名数组名字符串字面量初始化数组。六.指针函数和函数指针指针函数本质是函数其返回值是指针函数指针本质是指针变量存储函数的入口。函数指针指针函数声明语法类型 *函数名(参数列表)类型 *指针名参数列表重点强调函数返回什么类型的指针强调指针指向声明类型的函数占用空间代码占代码段空间指针本身占sizeof字节常见用途返回动态分配的内存地址返回数组或字符串的首地址返回结构体指针回调函数函数作为参数传递实现跳转表或状态机调用直接调用int *resultfunc(1,2);先赋值pfunc;或pfunc;在调用:(*p)(1,2);或p(1,2);七.二级指针一级指针int *p存储变量的地址二级指针(int **p)存储以及指针的地址。1.主要用途1.函数内修改指针本身如果想在函数中改变指针的指向必须传入二级指针。否则形参是实参的拷贝函数内改变不了外部的指针。2.动态二维数组创建行列可变的二维数组时常见做法是先分配指针数组再给每个指针分配一行。3.指针数组的操作遍历、排序字符串数组时往往使用二级指针遍历修改数组元素的顺序只需交换指针效率更高。4.链表头结点快速修改在链表插入、删除时使用二级指针可以简洁地处理头部变化不必分情况讨论。5.给指针返回值传递信息某些函数需要返回错误码同时又要输出指针结果此时错误码做返回值指针结果用二级指针传出。八.野指针野指针是指指向未知、无效或随机内存地址的指针。野指针和空指针NULL明确指向地址0不同野指针指向的地址是不可预测的。成因1.指针未初始化当指针声明时未赋予初始量他会赋予一个随机的内存地址直接使用未初始化的指针可能会访问到不合法的内存区域造成不可预计的后果。int *ptr; //未初始化的指针指向随机地址 *ptr10;2.指向的内存已被释放当指针指向的内存被释放时该指针依然保留了原来的地址该地址就不再有效指针也成为野指针如果再次访问该地址会造成不可语句的后果。int *ptr(int *)malloc(sizeof(int)); *ptr10; free(ptr); //释放了指针指向的内存 *ptr20;3.指针越界指针指向了合法分配的内存块但通过算术运算跑到了该内存块的外部。虽然地址在进程空间内但该地址不属于当前数组/对象访问它是非法的。int arr[5]; int *parr; pp10; //超出数组的内存范围 *p10000; //可能覆盖其他变量产生难以追踪的bug规避方法1.初始化时置为 NULL定义指针时可以先赋值为 NULL。使用前可使用 if (p ! NULL)检查。2.释放内存后立即置 NULLfree(p) 后野指针仍保留原地址必须手动 p NULL;。3.避免返回局部变量的地址函数内的局部变量在函数返回后已销毁不要返回其地址。4.注意指针作用域尽量缩小指针作用域或在嵌套块中使用完及时处理。5.使用静态/动态分析工具如 valgrind、编译器的 -Wall -Wextra 警告选项或 Address SanitizerASan。