C/C++的指针与函数(指针函数与函数指针辨析)

发布时间:2026/9/15 21:48:41

C/C++的指针与函数(指针函数与函数指针辨析) 文章目录指针函数语法指针函数的工程应用---malloc与new函数指针函数的地址函数的地址与函数的返回值函数指针的声明使用函数指针的调用函数函数指针数组使用typedef为函数指针取别名函数指针的工程应用---回调函数callback指针函数指针函数就是指针的函数是个函数是一个返回值为指针的函数。什么叫指针的函数呢为了便于理解笔者自己引入了一下的概念。注意这只是便于理解而且是笔者自己想的如果有问题还请指正首先先从函数来说起函数可以分为两种一种是有返回值的一种是没有返回值的void声明。在有返回值这一类函数中有int、float等等因为这一类函数执行完毕后相当于他的返回值占据了函数名称的位置如下intadd(inta,intb){returnab;}intmain{intcadd(1,2);//这里相当于12这个int值占据了add(1,2)这个位置}所以我们可以说例子当中的函数是个int的函数。明确了这个概念之后接下来我们来理解指针的函数这个概念指针的函数也就是说函数的返回值是个指针。一个指针代替了原来函数的位置。语法返回类型* 函数名(参数列表);例如int*createArray(intsize){int*arr(int*)malloc(size*sizeof(int));for(inti0;isize;i){arr[i]i1;//这里的arr[i]相当于*(arri)如果不懂可以去看我的上一篇博客}returnarr;// 返回指向动态分配数组的指针}函数的返回值属性是一个指针这个指针的类型属性是整型int。指针函数的工程应用—malloc与newmalloc作为内存分配的重要函数其在stdlib.h文件中函数原型是void*malloc(size_t);malloc就是一个返回值为void*的指针的指针函数。new函数同理函数原型如下void*operatornew(size_t);new也是一个返回值为void*的指针的指针函数。函数指针函数指针可以理解为函数的指针是个指针是一个指向函数的指针。函数的地址指针和地址是分不开的我们要理解函数的指针首先先要了解函数的地址。在程序编译时函数的地址是如何确认的呢首先编译器将每个源文件.c 或 .cpp编译成目标文件.o 或 .obj时在目标文件中函数的地址是相对于文件起始位置的偏移量相对地址这是因为编译器还不知道最终的内存布局无法确定函数的绝对地址。在链接时链接器将多个目标文件合并成一个可执行文件.exe 或 .elf链接器会为函数确定一个最终的地址。函数的函数名就是函数的地址。#includestdio.hvoidmyFunction(){printf(Hello, World!\n);}intmain(){printf(Address of myFunction: %p\n,(void*)myFunction);return0;}输出结果为Address of myFunction:0x400526我们知道在编译完成后函数地址已经确认了接下来我们看一下在程序的执行过程中程序如何通过地址来调用执行函数。程序执行的本质时PC程序指针计数器不断加一运行所指向内存单元的代码。当程序执行到函数部分时即PC程序指针计数器指向函数时发生函数调用。第一步调用者将参数压入栈中。参数压入顺序通常根据调用约定不同压入的方式也不同常见的有cdecl、stdcall、fastcall第二步调用者将返回地址函数的下一条指令的地址压入栈中。第三步PC程序指针计数器会更新为被调用函数的地址从而跳转到函数的代码段执行。第四步函数即被调用者创建栈帧由序言代码执行完成将调用者的栈帧指针ebp压入栈。设置新的栈帧基址ebp esp。为局部变量分配空间保存重要寄存器的值第五步开始执行函数代码。第六步执行完毕后将返回值存储到寄存器当中。释放栈帧恢复调用者的栈帧指针。第七步从栈中弹出返回地址到PC指针计数器当中。至此函数调用完成程序继续执行。额外提及一点在单片机的中断执行过程中与程序函数调用过程类似可以帮助理解。函数的地址与函数的返回值辨析函数的地址与函数的返回值program(function1);//这里传入program函数的是function1函数的地址program(function1());//这里传入program函数的是function1函数的返回值二者仅有一对括号的差别注意辨析。第一个是为了在program函数中调用function1函数第二个是先调用function1函数如何将function1函数的返回值当作参数输入到program函数中去。函数指针的声明函数的函数名就是函数的地址这一点已经被我们反复提及如下intfunction(inta,intb);function就是地址接下来我们来看函数指针的声明intfunction(inta,intb);int(*pf)(inta,intb);pffunction;这里的pf就是函数function的指针这时候大家可能会有疑惑function是个地址*pf所替换的是个地址的位置而pf却被解释为了指针对指针进行 * 运算解应用运算不应该得到的是pf这个指针所存储的地址的指向的内容的值吗为什么是个地址这里笔者来给出解释上文提过函数名就是函数的地址这一点是不变的但是由于函数本身是在程序段进行的存储也就是代表着对于“函数”的理解绝不能等同于数据的理解实际上函数名代表着函数本身也是代表着函数的指针也就是说function *function function这三者是完全等价的存在。上面说的可能有点晕我们也可以这么来理解函数指针首先来看数据指针的定义过程。inta0;int*pa;paa;这里*并没有解引用的含义有的仅仅是声明的含义。同理在函数指针的定义过程中*也没有解引用的含义有的仅仅是声明的含义。intfunction(inta,intb);int(*pf)(inta,intb);pffunction;//这里将function理解为了函数的地址还有一种理解方式将function理解为函数本身。function是函数那么(*pf)也是函数(*pf)是个函数pf就是指针对指针进行解引用就得到了函数也就是(*pf)。intfunction(inta,intb);int(*pf)(inta,intb);pffunction;//这里将function理解为了函数本身讲到这里笔者也成功的将自己绕晕。其实本质上函数函数的地址函数的指针都是函数名这三者是一个东西。就是因为函数是存储在程序段当中的所以无法将函数的地址运算方式同数据的地址运算方式对等起来。function *function function使用函数指针的调用函数intadd(inta,intb);intmain(){int(*pf)(inta,intb);pfadd;inttest1add(1,2);inttest2(*pf)(1,2);//使用*来对指针进行解引用得到函数return0;}intadd(inta,intb){returnab;}实际上下面这种调用函数也是正确的inttest3pf(1,2);//pf为指针存储的是地址地址即为函数名在这里 C Primer Plus给出了解释感觉这种设计真的是难以理解不过确实也证实了上面提及的函数函数的地址函数的指针都是函数名这三者是一个东西函数指针数组语法返回类型(*函数指针[n])(参数列表);//例子void(*funcPtrArray[3])();关于为啥在这个位置加上[ n ]我认为 C Primer Plus这里的这个解释很好我之间粘到下面。使用typedef为函数指针取别名常见的typedef的使用方法为typedef原类型 新类型名;但是在函数指针这里略微不同其语法格式为typedef返回类型(*函数指针)(参数列表);typedefvoid(*PF)(int);// 定义 PF为函数指针类型的别名实际上在这里原类型是返回类型 ( * )(参数列表)即void ( * )(int)而新类型名则是对应函数指针即PF。这样我们在创建一个void ( * )(int)形式的函数指针时就可以使用如下的形式PF pf;//相当于以下形式typedefvoid(*pf)(int);函数指针的工程应用—回调函数callback// 回调函数类型typedefvoid(*Callback)(int);// 接受回调函数作为参数的函数voidprocess(intvalue,Callback callback){printf(Processing value: %d\n,value);callback(value);// 调用回调函数}// 回调函数实现voidprintValue(intvalue){printf(Callback: %d\n,value);}intmain(){process(10,printValue);// 传递函数指针return0;}上述例子中使用了函数指针的方式创建回调函数还有一种弱定义__weak的方式相信学过STM32单片机HAL库一定不会对回调函数陌生在HAL库中就是这种弱定义__weak的方式。这篇文章主要是将函数指针的所以在这里就不多赘述关于弱定义的内容了。为什么要使用回调函数为了实现低耦合编程代码之间的耦合度过高会导致难以移植修改等问题使用回调函数就可以将部分的实现分离出去比如在单片机中中断处理的函数就是以回调函数的形式出现的用户只需要修改回调函数满足功能即可不用修改整个中断服务函数
延伸阅读

更多相关文章

2026/9/15 21:48:41

C/C++的指针与常量const

指针与常量常规变量的地址赋给const修饰的指针指向常量的指针指针常量指向常量的指针常量总结const修饰的常量的地址赋给const修饰的指针指向常量的指针指向常量的指针常量总结常规变量的地址赋给const修饰的指针 注意,以下三种情况中的例子中的a和b都没有被const修…

2026/9/15 21:43:40

虚拟机中zynq下BRAM读写和网口测试

文章目录一、内容介绍二、实现步骤2.1 petalinux和vivado配置步骤2.1.1 vivado配置2.1.2 petalinux配置2.2 linux系统(串口)2.2.1 u-boot系统2.2.2 linux系统2.3 petalinux和vivado相关2.3.1 TCP服务器程序2.3.2 BRAM读写一、内容介绍 ZYNQ的PL端读写BR…

2026/9/15 22:28:48

2026大折叠屏选购指南:从能用到该买的分水岭

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/15 22:28:48

DeepSeek Harness通用设置与Agent预设详解:从配置到实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/15 22:23:47

Claude Code 终端 Agent 实操:自主执行测试、排查编译错误并提交 PR

Claude Code 终端 Agent 实操:自主执行测试、排查编译错误并提交 PR随着 AI 辅助开发工具向终端命令行下沉,Claude Code 等终端 Agent 具备了直接感知整个工程上下文、执行任意 Shell 指令、捕获错误输出并自主发起 Git 提交的能力。相较于图形界面内的代…

2026/9/15 4:54:30

拯救者Y7000黑屏故障排查与维修实战指南

1. 项目概述:一台黑屏的拯救者Y7000,到底卡在哪一步? 联想拯救者Y7000系列笔记本,从2018年第一代搭载i5-8300H开始,到后来的i7-9750H、i7-10750H、i5-11400H,再到2023年款的R7-7840HS,它始终是学…

2026/9/15 0:01:16

AI英语单词APP开发:自适应学习算法与移动端优化实践

1. 项目概述 作为一名在移动应用开发领域摸爬滚打多年的老手,我最近完成了一个AI英语单词APP的开发项目。这个项目将传统单词记忆方法与现代AI技术相结合,打造了一款能够智能适应不同用户学习习惯的英语学习工具。 市面上大多数单词APP都存在一个通病&a…

2026/9/15 0:01:16

Flutter与OpenHarmony结合开发手语学习APP实战

1. 项目背景与核心价值作为一名同时接触过Flutter和OpenHarmony的开发者,最近我完成了一个基于Flutter for OpenHarmony的手语学习APP实战项目。这个项目最大的特点在于实现了跨平台框架与国产操作系统深度结合的创新实践——用Flutter开发的应用能完美运行在OpenHa…

2026/9/15 0:01:16

六个月成为机器人工程师:从ROS2到SLAM的实战路径

1. 六个月的紧迫感从哪来:先搞清楚你要成为哪种机器人工程师说实话,六个月的期限并不是一个宽松的时间线。市面上任何一本正经的机器人学教材都超过五百页,ROS2的官方文档可以翻到你怀疑人生,再加上ABB、KUKA这些工业机器人厂家动…

2026/9/15 14:22:53

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/15 21:31:11

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/15 11:42:23

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
咨询二维码