指针易错总结

📅2026/7/13 3:03:05 👁️次浏览
指针易错总结
一、核心铁律数组退化必考基础1. 数组【不退化】仅2种场景只有以下两种写法数组保留完整本体不会转为指针sizeof(数组名)数组名此时可以计算数组整体占用字节保留完整长度信息。2. 其余全部场景自动退化只要出现赋值、函数传参、存入数组、参与指针运算数组名自动退化为首元素地址普通指针丢失数组长度、整体信息。退化演示代码#includestdio.hintmain(){intarr1[3]{1,2,3};// 不退化完整数组本体printf(sizeof(arr1) %zu\n,sizeof(arr1));// 输出12// 退化数组名转为首元素地址int*parr1;printf(sizeof(p) %zu\n,sizeof(p));// 输出864位系统指针大小return0;}输出sizeof(arr1) 12 sizeof(p) 8结论数组退化后无法通过指针获取原数组长度。3. 经典误区不能对退化后的指针使用sizeof求数组长度必须在原始数组定义处提前计算保存。二、数组指针 和 指针数组黑马PPT原文整理1. 数组指针指向数组的指针是指针作用操作整块连续二维数组跨行遍历普通一级指针int* p arr;指向单个int元素步长4字节仅能遍历单行行数组指针int (*p)[5] arr;专门指向一整行5个int的一维数组步长5*420字节一次跳转完整一行内存适配标准规整二维数组int arr[3][5]数组指针完整遍历代码#includestdio.hintmain(){// 整块连续标准二维数组intarr[3][5]{{1,2,3,4,5},{11,22,33,44,55},{111,222,333,444,555}};// 数组指针指向一行5个intint(*p)[5]arr;for(inti0;i3;i){for(intj0;j5;j){printf(%d ,*(*pj));}printf(\n);p;// 步长20字节直接跳到下一行开头}return0;}运行结果1 2 3 4 5 11 22 33 44 55 111 222 333 444 5552. 指针数组存放指针的数组是数组作用存储多个独立一维数组的首地址实现不规则多行结构定义格式int* arr[2] {arr1, arr2};数组内每一个元素都是int*一级指针各行原始一维数组内存不连续每行长度可以不一样退化后无法计算单行长度必须手动提前保存每行长度指针数组二级指针完整遍历代码#includestdio.hintmain(){// 3个独立、内存不连续的一维数组intarr1[3]{1,2,3};intarr2[5]{4,5,6,7,8};intarr3[2]{9,10};// 指针数组存放各行数组退化后的首地址int*arr[3]{arr1,arr2,arr3};// 手动保存每行真实长度intlen[3]{3,5,2};// 二级指针匹配数组内元素类型int*int**parr;for(inti0;i3;i){for(intj0;jlen[i];j){printf(%d ,*(*(pi)j));}printf(\n);}return0;}运行结果1 2 3 4 5 6 7 8 9 10二级指针定义原理你之前总结的逻辑int** p内层int*匹配指针数组arr内部存储的数据类型第二个*标记这是二级指针专门指向int*一级指针三、核心难点数组指针中 p、*p、p 完整拆解1. p类型int (*)[5]行数组指针存储内容整行数组的起始地址代表含义一整行一维数组运算步长完整一行20字节负责跨行切换2. *p你最疑惑的核心点*p 取出p指向的一整行数组本体如arr[0]数组本体进入表达式自动退化最终结果普通int*指针等于本行第一个元素地址arr[0][0]关键结论p和*p在内存中地址数值完全相同但类型、步长、功能天差地别p步长20字节跨行使用*p步长4字节本行内遍历元素使用⚠️*p只是地址不是元素数值3. *(*p j)*p j本行第j个int元素的内存地址外层*再次解引用取出地址中存储的真实数字元素值4. p一次性跳过完整一行20字节内存指针定位到下一行数组开头实现换行遍历。必背通俗口诀p 管行整行地址、负责换行*p 管列起点取出数组本体 → 退化为本行首地址*(*pj) 管数值打印输出的真实数据p 负责换行四、函数指针demo17截图内容1. 概念函数指针专门存储函数入口地址的指针变量可以通过指针间接调用函数。标准语法返回值类型 (*指针名)(参数列表);⚠️ 括号不能省略省略会变成普通函数声明。完整可运行代码#includestdio.h// 无参无返回值函数voidmethod1(){printf(method1\n);}// 两个int参数、int返回值函数intmethod2(intnum1,intnum2){returnnum1num2;}intmain(){// 定义无参函数指针void(*p1)()method1;// 定义带两个int参数的函数指针int(*p2)(int,int)method2;// 通过函数指针调用函数p1();intnump2(10,20);printf(%d\n,num);return0;}运行输出method1 302. 易错区分❌int *p(int,int)普通函数声明返回值是int*不是指针✅int (*p)(int,int)函数指针p存储函数地址五、函数指针数组demo18计算器截图内容1. 概念函数指针数组数组中每一个元素都是同类型函数指针批量管理多个功能函数常用于菜单选择、计算器调度。标准语法返回值类型 (*数组名[长度])(参数列表);计算器完整代码#includestdio.h// 四则运算函数intadd(inta,intb){returnab;}intsubtract(inta,intb){returna-b;}intmultiply(inta,intb){returna*b;}intdivide(inta,intb){returna/b;}intmain(){// 函数指针数组存放4个运算函数地址int(*arr[4])(int,int){add,subtract,multiply,divide};intnum1,num2,choose;printf(请录入两个数字参与计算\n);scanf(%d%d,num1,num2);printf(请录入一个数字表示要进行的计算(1加法/2减法/3乘法/4除法)\n);scanf(%d,choose);// 根据下标调用对应函数intres(arr[choose-1])(num1,num2);printf(计算结果%d\n,res);return0;}运行示例请录入两个数字参与计算 10 20 请录入一个数字表示要进行的计算(1加法/2减法/3乘法/4除法) 1 计算结果30六、全书全部易错点你所有踩过的坑易错1误以为 *p 是元素数值❌*p是地址/指针不能直接打印数字✅*(*pj)双层解引用才是真实存储的int数值易错2分不清数组指针、指针数组int (*p)[5]数组指针单个指针规整二维数组跨行用int* arr[]指针数组数组存一级指针不规则多行易错3数组退化后使用sizeof求长度退化后的指针丢失数组长度不能用sizeof(arr[i])计算单行长度必须提前保存原始数组长度。易错4地址数值相同功能相同p和*p打印地址数值一致但类型、指针步长完全不同运算行为完全不一样。易错5数组指针遍历忘记写 p指针永远停留在第一行循环会重复打印第一行数据。易错6函数指针定义漏括号int *p(int,int)不是函数指针是函数声明必须写成int (*p)(int,int)。易错7二级指针类型不匹配指针数组内部元素是int*接收它的指针必须是int**不能用int*或数组指针。七、最终超级浓缩总结数组本体仅sizeof、两种场景保留其余全部退化为首元素地址数组指针int (*p)[N]单个指针步长一整行适配连续规整二维数组指针数组int* arr[]数组存储一级指针适配不规则多行搭配int**二级指针遍历p管整行*p为本行首地址双层解引用才是元素值p实现跨行函数指针返回值(*p)(参数)存储函数地址间接调用函数函数指针数组批量存放同类型函数实现菜单、多分支调度地址数值相等 ≠ 指针类型、步长、功能相同。