news 2026/9/24 16:05:25

从 Android 回调到 C 接口:函数指针 + void* self 的一次彻底理解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
从 Android 回调到 C 接口:函数指针 + void* self 的一次彻底理解

一、为什么学 C 回调会觉得“抽象”?

很多 Android / Java 开发者在第一次接触 C 回调时,都会被下面这种代码劝退:

typedef struct { void (*open)(void* self); void (*close)(void* self); } Ops;

看起来既不像函数,也不像类,更不像接口。

但当我真正理解它之后,才发现:

这套写法,本质上和 Android 的接口回调是完全一致的,只是语法层面更底层。

二、从 Android 的 setOnClickListener 说起

在 Android 中,我们非常熟悉这种写法:

button.setOnClickListener(listener);

这里发生了三件事:

  1. listener是一个对象实例

  2. 它实现了OnClickListener接口

  3. 系统在合适的时机回调:

listener.onClick(view);

关键点在于:

  • 系统并不关心 listener 的具体类型

  • 只关心:你有没有实现规定的方法

三、C 语言里没有接口,那怎么办?

C 语言没有:

  • class
  • interface
  • this

但系统级代码同样需要:

  • 回调
  • 解耦
  • 多态

于是 C 选择了一种“手工实现接口”的方式:

struct + 函数指针

四、Ops:C 里的“接口定义”

typedef struct { void (*open)(void* self); void (*close)(void* self); } Ops;

这段代码不是在实现逻辑,而是在做一件事:

定义一组能力约定(接口)

可以直接在脑子里翻译成 Java:

interface Ops { void open(); void close(); }

区别只有一个:

  • Java 有隐式this
  • C 需要显式传self

五、void* self 是什么?

void* self的本质是:

C 版的 this / Context / 上下文对象

因为 C 没有对象模型,所以:

  • 谁在调用

  • 状态在哪里

都必须由调用方手动传入

void file_open(void* self) { File* f = (File*)self; printf("%d\n", f->fd); }

这和 Java 里的:

void open() { System.out.println(this.fd); }

在“角色”上是完全等价的。

六、接口是怎么“绑定实现”的?

很多人会问:

file_open为什么会成为open的实现?

答案不在函数名,而在赋值

Ops file_ops = { .open = file_open, .close = file_close };

这一刻发生了绑定:

  • file_ops.open指向file_open

  • file_ops.close指向file_close

之后的调用:

file_ops.open(&f);

等价于:

file_open(&f);

七、file_ops 是什么角色?

可以非常准确地说:

file_ops是一个“接口实例 / 方法表 / 回调对象”

它就像 Android 里的:

OnClickListener listener = new MyClickListener();

只不过在 C 里:

  • 方法表(Ops)

  • 对象实例(self)

被拆开保存。

八、一句话总结构成完整模型

**C 的接口回调 = struct(接口定义)

  • 函数指针(方法)

  • void* self(this / Context)**

这并不是“奇怪的 C 写法”,而是:

在没有语言级支持的情况下,手工实现的面向对象与接口机制。

九、写在最后

当我把 C 的这套回调模型和 Android 的接口机制对齐之后,才真正意识到:

语言不同,但工程思想是完全相通的。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/24 13:01:26

中国辅助驾驶“新竞赛”打响,高智价比AI芯片如何定义新标杆?

中国辅助驾驶的落地竞速已经从单纯的性能比拼,进入“法规与体验双轮驱动”的全新阶段。一方面,包括中国在内的全球多个国家和地区已对车辆搭载AEB系统提出强制性要求,直接推动了组合辅助驾驶进入市场主导的“爆发期”。根据《高工智能汽车研究…

作者头像 李华
网站建设 2026/9/24 4:29:12

渔人的直感:FF14终极智能钓鱼计时器完全指南

渔人的直感:FF14终极智能钓鱼计时器完全指南 【免费下载链接】Fishers-Intuition 渔人的直感,最终幻想14钓鱼计时器 项目地址: https://gitcode.com/gh_mirrors/fi/Fishers-Intuition 还在为错过幻海流最佳时机而烦恼吗?是否经常因为分…

作者头像 李华
网站建设 2026/9/24 0:03:30

如何在30天内完成Agent有效性验证?生物药研发加速的秘密武器

第一章:生物制药Agent有效性验证的挑战与机遇在生物制药领域,智能Agent正逐步应用于药物发现、靶点识别和临床试验模拟等关键环节。然而,这些Agent的有效性验证面临诸多挑战,同时也孕育着重大技术突破的机遇。数据异质性与标准化难…

作者头像 李华
网站建设 2026/9/24 13:47:25

为什么顶尖金融机构都在重构Agent审计日志?背后隐藏的4大合规趋势

第一章:金融合规 Agent 的审计日志在金融行业,系统操作的可追溯性是合规性的核心要求之一。审计日志作为记录系统行为的关键组件,为监管审查、异常检测和责任追溯提供了数据基础。金融合规 Agent 通过自动化手段收集、分析并归档关键操作事件…

作者头像 李华
网站建设 2026/9/24 4:22:32

58、Linux与Ubuntu:深入解析与使用指南

Linux与Ubuntu:深入解析与使用指南 1. mkinitrd工具介绍 mkinitrd工具会查看 /etc/fstab 、 /etc/modprobe.conf 和 /etc/raidtab 文件,以获取在启动过程中确定需要加载哪些模块所需的信息。在我们的系统中,使用以下命令: # mkinitrd initrd-2.6.7-1.img 2.6.7-1…

作者头像 李华
网站建设 2026/9/24 3:05:35

60、Ubuntu安装规划与硬件要求全解析

Ubuntu安装规划与硬件要求全解析 1. 选择Ubuntu的优势与安装关键因素 Ubuntu作为一种计算解决方案有诸多优势,由于其丰富的软件资源,它能在不同层级和硬件平台上扮演多种角色。为确保安装成功,需关注以下关键因素: - 准备工作 :充分讨论迁移或部署事宜,明确其带来的…

作者头像 李华