本文将大量涉及C语言高级操作,如函数指针、结构体指针、二级指针、指针频繁引用解引用、typedef、static、inline和C语言项目结构等知识,请确保自己不会被上述知识冲击,如果没有这顾虑,请尽情享受~

摘要:

本文探讨在C语言中模拟面向对象编程(OOP)的"一点五编程"技术,通过函数指针、结构体嵌套和二级指针强制转换实现类、接口与多态。开发流程分声明(接口/类结构体、类型转换函数)、实现(方法绑定、初始化)和使用三阶段,强调方法集指针必须位于类结构体首地址以实现动态绑定。该方法将硬件驱动与业务逻辑解耦,结合嵌入式场景展示模块化设计,附伪实现循迹小车项目验证继承特性,为C语言赋予OOP的封装性、扩展性,提升嵌入式代码可维护性。

渊源

一开始时候,我是不知道这个技术的。在某一天我在刷B站的时候,看到一个作者为“一点五编程”的视频。他提出了一种编程思想,命名为“一点五编程”。其中:

"一"指的是模块化思想

“点五”指的是(*p)->f(p)技巧

我一看,好像是一种高端的技巧哇,于是开始看他的视频,发现讲解这一技术核心的视频全都是充电的!!!好吧,那我只好翻你文档看了,找到了他的个人博客。唔,好像什么都写了,但好像少点什么,,,哦,没教我到底怎么组织文件。

然后我继续翻网页,在CSDN上发现三篇文章,讲的是对“一点五编程”的解读。但是后来在自己实操过程中,还是发现了其中的错误。

看了这么多文章和视频,脑子一拍,这不就是面向对象的编程范式吗,只不过C语言是面向过程的语言,没有现成的面向对象的组件,但是思想上完全就是OOP那套嘛!

于是我开始自己扒,终于,也是让我扒出来了~

在文章的最后我会放上一个循迹小车的项目,当然,功能上伪实现(狗头),那接下来先讲下这门技术的基本理论和开发流程吧

理论

面向对象编程

我们先来说一下面向对象编程是什么:

面向对象编程是一种编程范式,它通过定义类和对象来组织和设计程序。

在面向对象编程中,程序猿通过创建类来定义数据结构和行为,通过创建对象来实例化这些类,并通过对象之间的交互来实现程序的功能。这种方法使得程序的结构更加清晰和易于维护。

面向对象编程有几个特性,分别是:封装、继承、多态、抽象,这里就不再说了,只要知道本文会体现就行,(纠结)因为毕竟还是挺难理解的,我也讲不明白,可以看看别的大佬的文章。

为什么要把面向对象编程拉出来说呢?众所众知,嵌入式是一个软硬件结合的学科,这就会存在一个问题,就是我们会非常在乎硬件的实现,上层的功能实现就实现了,也不会在乎开发的结构、后期的维护等,这一点在初学者身上体现的淋漓尽致。

而面向对象编程就致力于让程序更加模块化,通过继承和多态,使得大量代码复用,它还有模拟现实世界中对象和关系的能力。这样,就为开发者提供了一种自顶向下的开发思路。同时,它将上层实现与下层驱动相隔离,让更换开发平台变得简单。

流程

我将整个C语言面向对象编程的开发分为三个阶段,分别是声明、实现和使用。

声明

声明阶段又可以分为五个步骤,这些都是在头文件中写入的,分别是:

  1. 声明接口函数
  2. 定义接口结构体
  3. 定义类结构体
  4. 定义类型转换内联函数
  5. 声明方法实例

    其中,声明接口函数、定义接口结构体和定义类型转换内联函数仅需书写一次,另外两个步骤可以根据实际需求定义更多的类和方法实例。

声明接口函数

在这里,接口就是类的行为方法集,控制整个类的行为方式。以循迹模块为例,读取循迹信息就是它的一个行为;以电机驱动模块为例,控制电机停转、正转、反转和控制转动速度就是它的一系列行为。我们首先要思考我们所抽象出的类有哪些行为方法,写成下面形式:

typedef int (*Method0FnT)(void* self, ...);
typedef int (*Method1FnT)(void* self, ...);
.
.
.
typedef int (*MethodnFnT)(void* self, ...);

定义接口结构体

接下来,我们要将上面的接口函数放到一个接口结构体中,方便由各个类使用:

typedef struct
{
Method0FnT method0Fn;
Method1FnT method1Fn;
.
.
.
MethodnFT methodnFn;
} MethodsT;

定义类结构体

完成上面步骤后,一个类的方法集就总结好了,再由方法集和类的各个属性组成完整的类,这里一定要注意,方法集指针一定要放在类结构体的第一个,否则会出现错误:

typedef struct
{
MethodsT* methods;
Type attribute0;
Type attribute1;
.
.
.
Type attributen;
} Class;

定义类型转换内联函数

这里是我们实现多态这一特性最核心的步骤,写成如下格式:

static inline int method0Fn(void* self, ...)
{
return (*(MethodsT**)self)->method0Fn(self, ...);
} static inline int method1Fn(void* self, ...)
{
return (*(MethodsT**)self)->method1Fn(self, ...);
} .
.
. static inline int methodnFn(void* self, ...)
{
return (*(MethodsT**)self)->methodnFn(self, ...);
}

使用上面的语句,我们能够将(*p)->f(p)改写为f(p)的形式,而且不需要管类的具体函数实现。这里我们将指向类的一级指针强制类型转换为指向接口的二级指针,再解引用就得到了一个仅指向接口的一级指针,再用成员访问符使用接口函数。

这个过程中,要将指向类的一级指针强制类型转换为指向接口的二级指针,就需要类的起始地址与接口的起始地址相同,也就是为什么上面说方法集的指针一定要放在类结构体的第一个,这样指向接口的二级指针解引用后才会指向接口。

声明方法实例

上面定义了抽象的接口和类,该到这个接口函数的具体实现了,当然,还要写上类初始化函数的声明:

int classMethod0(void* self, ...);
int classMethod1(void* self, ...);
.
.
.
int classMethodn(void* self, ...); int classInit(void* self, ...);

实现

头文件内容就完成了,下面是具体的相关函数的实现了,下面部分都在源文件中写入,分为三个步骤:

  1. 定义方法实例
  2. 定义接口实例
  3. 定义类初始化函数

    三个步骤的内容均由头文件中的声明限制。

定义方法实例

方法的实例我们已经在头文件中声明过了,在这里我们进行这些方法实例的定义:

int classMethod0(void* self, ...);
{
Class* pClass= (Class*)self; //具体内容实现
//异常处理 return 1;
} int classMethod1(void* self, ...)
{
Class* pClass= (Class*)self;
...
return 1;
} .
.
. int classMethodn(void* self, ...)
{
Class* pClass= (Class*)self;
...
return 1;
}

定义接口实例

具体的方法已经有了,接下来我们要实现具体的接口了,将方法实例的函数指针传入到接口结构体中:

static MethodsT classMethods=
{
.method0Fn= classMethod0,
.method1Fn= classMethod1,
.
.
.
.methodnFn= classMethodn
}

定义类的初始化函数

最后我们编写所需类的初始化的函数,类的属性值将通过初始化函数传入:

int classInit(void* self, ...)
{
Class* pClass= (Class*)self; pClass->methods= &classMethods;
pClass->attribute0= ...;
pClass->attribute1= ...;
.
.
.
pClass->attributen= ...; //其他初始化内容
//异常处理 return 1;
}

使用

使用起来就简单了,首先我们要生成类的实例,然后使用初始化函数进行类初始化,然后,使用!

//生成实例可以放在main.h中或者主函数前或者主函数开头
Class class
//初始化要放在开头
classInit(&class, ...);
//使用方法就放在任何你需要其执行的地方即可
method0Fn(&class, ...);
method1Fn(&class, ...);
.
.
.
methodnFn(&class, ...);

就这样,我们的所有功能就实现啦,我相信你一定学会了!(狗头)

附录

循迹小车的伪实现,会体现出上面没有提及的继承特性,见本仓库:

https://github.com/swfeiyu/coop

关于我们更多介绍可以查看云文档:Freak嵌入式工作室云文档,或者访问我们的wiki:https://github.com/leezisheng/Doc/wik

C语言一点五编程实战:纯 C 的模块化×继承×多态框架的更多相关文章

  1. Python源代码 -- C语言实现面向对象编程(基类&派生类&多态)

    背景 python是面向对象的解释性语言.然而python是通过C语言实现的,C语言怎么跟面向对象扯上了关系? C语言能够实现面向对象的性质? 原文链接:http://blog.csdn.net/or ...

  2. 《Go并发编程实战》读书笔记-初识Go语言

    <Go并发编程实战>读书笔记-初识Go语言 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 在讲解怎样用Go语言之前,我们先介绍Go语言的特性,基础概念和标准命令. 一. ...

  3. 使用C语言调用mysql数据库编程实战以及技巧

    今天编写使用C语言调用mysql数据库编程实战以及技巧.为其它IT同行作为參考,当然有错误能够留言,共同学习. 一.mysql数据库的C语言经常使用接口API 1.首先当然是链接数据库mysql_re ...

  4. Java多线程编程实战指南(核心篇)读书笔记(五)

    (尊重劳动成果,转载请注明出处:http://blog.csdn.net/qq_25827845/article/details/76730459冷血之心的博客) 博主准备恶补一番Java高并发编程相 ...

  5. 手把手和你一起实现一个Web框架实战——EzWeb框架(五)[Go语言笔记]Go项目实战

    手把手和你一起实现一个Web框架实战--EzWeb框架(五)[Go语言笔记]Go项目实战 代码仓库: github gitee 中文注释,非常详尽,可以配合食用 本篇代码,请选择demo5 中间件实现 ...

  6. 一步步分析:C语言如何面向对象编程

    这是道哥的第009篇原创 一.前言 在嵌入式开发中,C/C++语言是使用最普及的,在C++11版本之前,它们的语法是比较相似的,只不过C++提供了面向对象的编程方式. 虽然C++语言是从C语言发展而来 ...

  7. Linux下的C编程实战

    Linux下的C编程实战(一) ――开发平台搭建 1.引言 Linux操作系统在服务器领域的应用和普及已经有较长的历史,这源于它的开源特点以及其超越Windows的安全性和稳定性.而近年来, Linu ...

  8. Linux下的编程实战【转】

    一篇比较不错的文章, 降到了 makefile make , gcc编译器,GDB调试器, Linux文件系统,Linux文件API,.C语言库函数(C库函数的文件操作实际上是独立于具体的操作系统平台 ...

  9. 《Java多线程编程实战指南(核心篇)》阅读笔记

    <Java多线程编程实战指南(核心篇)>阅读笔记 */--> <Java多线程编程实战指南(核心篇)>阅读笔记 Table of Contents 1. 线程概念 1.1 ...

  10. 【算法】C语言趣味程序设计编程百例精解

    C语言趣味程序设计编程百例精解 C/C++语言经典.实用.趣味程序设计编程百例精解(1)  https://wenku.baidu.com/view/b9f683c08bd63186bcebbc3c. ...

随机推荐

  1. SAM 学习笔记

    发现自己根本没有 SAM 基础,所以想补一篇学习笔记. SAM SAM 是一个可以接受字符串 \(s\) 的所有后缀的最小 \(DFA\)(确定性有限状态自动机).不过他最大的用处和后缀数组一样,都是 ...

  2. DeepSeek本地部署

    一.ollama ollama是一个管理和运行所有大模型.开源大模型的平台.在官网的Models中可以看到deepseek-r1的AI模型 1.在官网中下载对应系统的ollama,下载时需要开墙,或者 ...

  3. 小米字体和思源宋体CSS调用使用指南

    小米字体 //css引用 <link rel="stylesheet" href="https://font.sec.miui.com/font/css?famil ...

  4. Win10、Win11老游戏运行补丁(cnc-ddraw),适用于红色警戒、帝国时代等经典游戏

    Win11.Win10老游戏运行补丁(cnc-ddraw),适用广泛,红色警戒(红警),直接复制到游戏目录,然后即可畅玩.再也不需要修改:管理员运行,兼容性运行,更改DPI.cnc-ddraw 可以修 ...

  5. Vue3组件通信全攻略:多种方式详解+实战场景,轻松玩转复杂数据流!

    一.组件通信为何如此重要? 在大型Vue项目中,组件通信如同神经网络般贯穿整个应用.良好的通信机制能: 实现组件解耦 提升代码可维护性 构建清晰数据流 支撑复杂业务场景 二.父子组件通信:核心通信模式 ...

  6. 从文件到块: 提高 Hugging Face 存储效率

    Hugging Face 在 Git LFS 仓库 中存储了超过 30 PB 的模型.数据集和 Spaces.由于 Git 在文件级别进行存储和版本控制,任何文件的修改都需要重新上传整个文件.这在 H ...

  7. Surpac 安装

    修改config文件 share 文件进行整体替换 数字地质 环境配置问题总结 *http 报错! 块体生成数据暂停,点击生成验收量提交,报错生成失败!!! 解决方案:重新打开块体

  8. python os.walk函数

    os.walk() 方法用于通过在目录树中游走输出在目录中的文件名,向上或者向下. root 所指的是当前正在遍历的这个文件夹的本身的地址 dirs 是一个 list ,内容是该文件夹中所有的目录的名 ...

  9. fs的proxy_media模式失效

    概述 freeswitch是一款简单好用的VOIP开源软交换平台. 在fs的使用过程中,某些场景只需要对rtp媒体做透传,又不需要任何处理. 在fs1.6的版本中,我们可以使用proxy_media来 ...

  10. 5个步骤完成 Vue3 开发调试工具安装教程

    Vue3 开发调试工具安装教程 5个步骤 第一步:点击浏览器右上角,更多工具 – 扩展程序 第二步:点击右上角 – 开发者模式 开关 第三步:点击 "添加已解压的扩展程序" 第四步 ...