指针重难点:从字符指针到函数指针与转移表

张开发
2026/4/17 14:33:29 15 分钟阅读

分享文章

指针重难点:从字符指针到函数指针与转移表
专栏C语言C语言指针4一.字符指针变量1.1基本用法1.2指向常量字符串1.3经典笔试题二.数组指针变量2.1定义格式2.2指针数组与数组指针2.3数组指针初始化三.二维数组传参的本质3.1形参两种等价写法3.2举例四.函数指针变量4.1函数地址4.2函数指针定义4.3函数指针调用4.4经典代码C陷阱与缺陷4.4.1经典14.4.2经典2typedef简化五.函数指针数组5.1定义格式六.转移表计算器的实现利用转移表总结易错点本文为 C 语言指针系列第四篇深度精讲字符指针、数组指针、二维数组传参本质、函数指针、函数指针数组、转移表六大核心考点我会以严谨底层逻辑帮助大家尽可能的吸收、消化并化为己用前言在前面的指针学习中我们已经掌握了指针基础、指针与数组、二级指针等核心内容完成了指针入门阶段的积累。而从本篇开始我们将正式进入指针高阶应用学习 —— 字符指针的底层存储、数组指针与指针数组的本质区分、二维数组传参的真实逻辑、函数指针与转移表的实战用法。这些内容既是理解 C 语言内存模型的关键也是笔试面试与工程开发的高频考点跟着本篇思路稳步学习就能顺利打通指针进阶的全链路。一.字符指针变量字符指针char*是C语言中使用最频繁的指针类型之一既可以指向单个字符也可以指向字符串。1.1基本用法intmain(){charchw;char*pcch;*pcw;return0;}pc存放字符变量ch的地址通过*pc可修改指向的字符1.2指向常量字符串intmain(){constchar*pstrhello bit.;printf(%s\n,pstr);return0;}核心要点并非把字符串存入指针而是将字符串首字符地址存入pstr。字符串常量存储在只读数据区不允许修改必须用const修饰。相同常量字符串在内存中只存一份。1.3经典笔试题#includestdio.hintmain(){charstr1[]hello bit.;charstr2[]hello bit.;constchar*str3hello bit.;constchar*str4hello bit.;if(str1str2)printf(str1 and str2 are same\n);elseprintf(str1 and str2 are not same\n);if(str3str4)printf(str3 and str4 are same\n);elseprintf(str3 and str4 are not same\n);return0;}运行结果代码分析str1、str2是数组各自独立开辟内存地址不同。str3、str4指向只读区同一个常量字符串地址相同。二.数组指针变量数组指针是指向数组的指针本质是指针专门存放整个数组的地址数组名。2.1定义格式int(*p)[10];逻辑分析p先与*结合说明是指针。指向一个包含 10 个int元素的数组。必须加括号因为[ ]优先级高于*。2.2指针数组与数组指针指针数组与数组指针的区分在上一章节我们已经讲过了这里再重复说明一遍int*p1[10];// 指针数组int(*p2)[10];//数组指针指针数组是数组里面每个元素是指针数组指针int(*p)[10]是指针指向整个数组区分口诀括号优先是指针括号靠后是数组2.3数组指针初始化intarr[10]{0};int(*p)[10]arr;·arr· 是整个数组的地址类型为 ·int(*)[10]·。一维数组极少使用二维数组传参是核心应用场景。三.二维数组传参的本质核心结论二维数组可以看作 “元素为一维数组” 的一维数组。二维数组名表示第一行一维数组的地址类型为数组指针。3.1形参两种等价写法写法1数组形式voidtest(inta[3][5],introw,intcol);写法2指针形式本质voidtest(int(*p)[5],introw,intcol);3.2举例#includestdio.hvoidtest(int(*p)[5],intr,intc){inti0;intj0;for(i0;ir;i){for(j0;jc;j){printf(%d ,*(*(pi)j));}printf(\n);}}intmain(){intarr[3][5]{{1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7}};test(arr,3,5);return0;}分析1.p i指向第i行。2.*(p i)第i行首元素地址。3.*(*(p i) j)第i行第j列元素。运行结果四.函数指针变量函数指针是指向函数的指针用于存放函数地址并通过地址调用函数。4.1函数地址函数名 函数地址函数名 函数名例如#includestdio.hvoidtest(){printf(hehe\n);}intmain(){printf(test: %p\n,test);printf(test: %p\n,test);return0;}4.2函数指针定义举例intAdd(intx,inty){returnxy;}//定义函数指针int(*pf)(int,int)Add;其中int函数返回值类型(*pf)pf是函数指针(int, int)函数参数列表4.3函数指针调用举个例子intmain(){int(*pf)(int,int)Add;printf(%d\n,(*pf)(2,3));//---标准调用printf(%d\n,pf(3,5));//---简化调用*可省略return0;}4.4经典代码C陷阱与缺陷4.4.1经典1(*(void(*)())0)();分析将0强制转为void(*)()类型函数地址调用 0 地址处的无参无返回值函数4.4.2经典2void(*signal(int,void(*)(int)))(int);分析声明signal函数参数int 函数指针void(*)(int)返回值函数指针void(*)(int)typedef简化typedefvoid(*pfun_t)(int);pfun_tsignal(int,pfun_t);五.函数指针数组将多个同类型函数地址存入数组即为函数指针数组。5.1定义格式举个例子int(*pArr[5])(int,int);分析pArr是数组每个元素为int(*)(int, int)类型函数指针但是有一个要求就是所有函数的返回值、参数个数、参数类型必须完全一致。六.转移表我们一般写简单计算器的实现的代码时就会想到使用switch语句来实现但是写完之后代码很长很麻烦效率低那我们如何解决这个问题呢那就是利用转移表了转移表用函数指针数组替代大switch/case代码更简洁、扩展性更强。接下来请看代码如何利用转移表来实现的计算器的实现利用转移表#includestdio.hintadd(inta,intb){returnab;}intsub(inta,intb){returna-b;}intmul(inta,intb){returna*b;}intdiv(inta,intb){returna/b;}intmain(){intx,y;intinput1;intret0;// 转移表int(*p[5])(int,int){0,add,sub,mul,div};do{printf(*************************\n);printf( 1:add 2:sub\n);printf( 3:mul 4:div\n);printf( 0:exit \n);printf(*************************\n);printf(请选择);scanf(%d,input);if(input1input4){printf(输入操作数);scanf(%d %d,x,y);ret(*p[input])(x,y);printf(ret %d\n,ret);}elseif(input0){printf(退出计算器\n);}else{printf(输入错误\n);}}while(input);return0;}运行结果优点代码简洁优雅新增功能只需添加函数与数组元素工程与面试高频用法总结char*指向字符串时存储首字符地址常量字符串必须用const修饰相同字符串只存一份。数组指针int(*p)[10]是指向数组的指针主要用于二维数组传参。二维数组名是第一行地址类型为数组指针传参时可用数组或指针形式接收。函数名即函数地址函数指针可存储并调用函数*可省略。函数指针数组可实现转移表替代冗长switch/case。typedef可大幅简化复杂指针类型的书写。易错点混淆指针数组与数组指针导致定义错误。二维数组传参误用int**造成程序崩溃。忽略字符串常量只读属性试图修改导致程序异常。函数指针参数 / 返回值不匹配编译失败。函数指针数组内存放不同类型函数引发未定义行为。如果这篇文章对友友们有帮助期待友友们的支持点赞 ❤️ 干货收藏助力指针吃透收藏 反复回看攻克难点盲区关注 持续更新C 语言不迷路留言 交流探讨学习共同进步友友们的每一次互动都是我持续更新C语言硬核知识的动力

更多文章