C++函数指针语法和类型

发布时间:2026/10/17 11:33:00

C++函数指针语法和类型 函数指针是 C/C 中一种将函数地址作为变量存储和传递的机制它允许程序在运行时动态选择并调用不同的函数是实现回调、策略模式和插件系统的核心基础。核心语法结构函数指针的声明语法遵循“返回类型 (*指针名)(参数列表)”的格式。例如声明一个指向“接收两个 int 参数并返回 int”的函数的指针int (*funcPtr)(int, int);int表示该指针所指向的函数返回值类型为 int。(*funcPtr)括号内的*表示这是一个指针funcPtr是指针变量名。(int, int)表示该指针只能指向参数为两个 int 的函数。赋值与调用函数名本身即代表其入口地址因此可以直接赋值给函数指针int add(int a, int b) { return a b; } funcPtr add; // 或 funcPtr add;调用时只需像普通函数一样使用指针变量int result funcPtr(3, 4); // result 7使用 typedef 简化类型为避免每次声明都写冗长的语法通常使用typedef或using定义别名typedef int (*MathFunc)(int, int); // C风格 using MathFunc int (*)(int, int); // C11风格 MathFunc funcPtr add;成员函数指针的特殊性当指向类的非静态成员函数时语法需额外指定类作用域并使用.*或-*运算符调用class Calculator { public: int multiply(int a, int b) { return a * b; } }; typedef int (Calculator::*MemFunc)(int, int); MemFunc memPtr Calculator::multiply;//获取成员函数的“偏移量” Calculator calc; int result (calc.*memPtr)(3, 4); // 绑定对象并调用必须绑定具体对象核心概念这里的memPtr不是传统意义上存储内存绝对地址的普通指针。因为类Class只是一个模板在没有创建具体对象之前multiply函数在内存中并没有一个确定的绝对位置。成员指针如Calculator::multiply本质上存储的是一个偏移量Offset或相对位置信息。当你写下calc.*memPtr时编译器在底层做的事情是获取对象calc的内存基地址 成员指针memPtr中记录的偏移量 真正要访问的函数或数据的内存地址。真相memPtr实际上存储的是multiply函数在Calculator类内存布局中的偏移量Offset信息。它就像是一张“户型图上的标记”告诉你“这个函数在对象的哪个位置”但必须结合具体的“房子”才能使用。因为memPtr只有偏移量没有具体的内存地址所以它绝对不能脱离对象单独调用即不能直接写memPtr(3, 4)。必须将它与一个具体的对象绑定.*运算符这是 C 专属的“指向成员的解引用运算符”。它的作用是把必需是一个具体的对象左操作数和必须是一个成员指针右操作数绑定在一起从而访问该对象的具体成员数据或函数通过对象基地址创建对象时分配给这个对象的内存起始地址 偏移量找到真正的函数并调用。为什么必须加括号(calc.*memPtr)这是 C 语法中的一个易错点。因为函数调用运算符()的优先级高于.*运算符。如果不加括号编译器会尝试先执行memPtr(3, 4)从而引发编译错误。加上括号后编译器会先组合对象和指针再执行函数调用。如果你的calc对象是一个指针例如Calculator* pCalc calc;那么你需要使用-*运算符并且同样需要括号int result (pCalc-*memPtr)(3, 4);总结这段代码的本质是“延迟绑定”。它把“要调用哪个函数”memPtr和“在哪个对象上调用”calc分开了直到(calc.*memPtr)这一步才将它们合体并执行。这种机制在实现回调函数、策略模式或消息分发机制时非常有用成员函数指针的类型更特殊例如typedef void(A::*PF)();这种指针不仅规定了参数和返回值还绑定了类A。它只能指向类A的成员函数。它不能指向全局函数。它也不能指向类B的成员函数即使签名完全一样。例子class A { public: void foo() {} }; class B { public: void foo() {} }; typedef void (A::*PtrA)(); typedef void (B::*PtrB)(); PtrA p A::foo; // ✅ PtrA q B::foo; // ❌ 错误这是 B 的函数不是 A 的为了让你更透彻地明白我们可以对比一下不加和加了typedef的区别1. 不加typedef它就是一个【指针变量】void (A::*p)();在这里p是一个变量名。编译器看到*p就知道你要在内存里开辟一个空间用来存放地址。如果你想再定义一个同类的指针你必须把这一长串再抄一遍void (A::*q)();。2. 加了typedef它变成了一个【类型别名】typedef void (A::*PF)();在这里PF是一个类型名就像int或char一样。编译器看到typedef它的任务就变成了“起外号”。它把void (A::*)()这一长串复杂的语法打包成了一个简单的名字PF。此时内存里没有分配任何空间也没有产生任何变量。以后你想定义这种指针只需要写PF p;或PF q;就行了。总结没有typedefvoid (A::*p)()是在造零件声明变量。有了typedeftypedef void (A::*PF)()是在画图纸定义类型。典型应用场景回调函数将函数指针作为参数传递给另一个函数实现“事后通知”机制。策略模式通过切换不同的函数指针动态改变算法行为。函数表/跳转表用数组存储多个函数指针根据索引快速调用对应函数常用于状态机或命令解析器。现代替代方案C11 引入了std::function和lambda表达式提供了更安全、更灵活的替代方案#include functional std::functionint(int, int) func [](int a, int b) { return a b; };虽然函数指针在性能上仍有优势无额外开销但在大多数现代项目中推荐优先使用std::function以获得更好的类型安全和可读性。
延伸阅读

更多相关文章

2026/10/16 14:12:30

Go服务生产环境的生存规则,你知道几条?

Go 让你很容易就能写出一段“能编译、能通过冒烟测试、一遇到真实流量就跪”的代码。这门语言默认给你很少——没有运行时异常可以捕获,没有框架护栏,没有魔法。这是个特性,但这也意味着:让一个服务在生产环境活下去的规则&#x…

2026/10/14 20:43:22

Open-Connector:开源连接器网关,解决AI Agent接入SaaS服务难题

【导语:Open-Connector作为面向AI Agent和应用开发者的开源连接器网关,旨在解决AI Agent接入外部SaaS服务的系列问题,提供丰富的连接器目录和预构建Actions,适用于多种类型项目。】开源网关解决AI Agent接入难题Open-Connector是一…

2026/10/17 11:30:19

Melon Google Books Downloader 环境搭建与排错指南

简介:Melon Google Books Downloader(MGBD)是一款面向研究者、学生及数字资源爱好者的小型桌面工具,专为合法获取Google Books平台上的预览内容而设计,解决用户无法直接下载整书PDF/EPUB的痛点,适用于文献调…

2026/10/17 11:30:19

Ultraedit使用:配置字数统计工具栏与TaoToken API Key统一管理

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

2026/10/17 11:30:19

从“只看不改”到高效审查:open-code-review 代码评审实践

从"只看不改"到"真审查":我如何用 open-code-review 把代码评审拉回正轨做代码审查这件事,大家心里都清楚它重要,但实际落地起来总是差点意思。我见过太多团队的 code review 流于形式——PR 挂着两三天没人理&#xff0…

2026/10/17 11:30:19

基于YOLO26的电瓶车与自行车检测系统开发实践

1. 项目整体设计与技术选型1.1 为什么做电瓶车与自行车检测这个项目的出发点其实很直接:我接到了一个模拟园区安防系统的需求,对方需要在监控画面里自动识别电瓶车和自行车,用来做非机动车流量统计和指定区域闯入预警。翻了一圈现成的算法方案…

2026/10/17 11:25:18

CSAPP malloc实验通关指南:堆内存管理核心原理与避坑清单

简介:这份资源围绕《深入理解计算机系统》(CSAPP)中的malloc实验展开,面向正在学习内存管理、准备完成动态内存分配器实现的学生与开发者。内容涵盖内存池管理、块结构设计、碎片处理、内存对齐、free与realloc机制等核心知识点,并配有实验分…

2026/10/16 5:33:43

Python调用Gemini Structured Outputs实现工单路由门禁

客服工单最怕的不是模型“答错一句话”,而是它给出一段看起来合理的说明,程序却从中猜错优先级。通俗做法是:要求模型只交 JSON(JavaScript Object Notation,轻量数据格式),再让代码验证它。Gem…

2026/10/16 4:03:48

Spring Boot超市进销存系统毕设实战:从需求拆解到答辩通关

最近带的一个学生项目组里,有A同学跑来问我:选什么毕设题目最稳妥,既能让评审老师觉得工作量够,又不会在答辩时被问到语无伦次。我第一反应就是推荐基于Spring Boot的超市仓库管理系统——也就是超市进销存系统。这个题目乍一看平…

2026/10/15 13:42:16

Flutter StatefulWidget 生命周期核心解析

很多刚开始接触 Flutter 的朋友,在看完一堆“Hello World”和基础组件之后,大概率都会撞上同一堵墙:StatefulWidget 里那堆 initState、build、dispose 方法,到底什么时候被调用?为什么顺序是那样?在里面到…

2026/10/17 0:04:32

PlanarSLAM:面向曼哈顿世界的点线面联合SLAM系统

简介:本资源是面向机器人视觉与SLAM研究者的C开源RGB-D SLAM系统实现,专为结构化室内场景(如办公室、走廊)设计,通过融合点、线、平面三类几何特征并引入“曼哈顿世界”假设,显著提升位姿跟踪鲁棒性与稠密地…

2026/10/17 0:04:32

TDH Workflow调度实战:从DAG依赖驱动到任务编排与故障排查

1. 从一个调度脚本说起:TDH Workflow到底在解决什么问题第一次接触TDH的Workflow,是在一个数据清洗项目里。当时团队每天要跑几十个ETL任务,任务之间有严格的先后依赖——A表清洗完才能跑B表的关联,B表跑完才能触发C表的聚合。最开…

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

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

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