c语言杂谈系列:模拟虚函数
从整体来看,笔者的做法与之前的模拟多态十分相似,毕竟c++多态的实现与虚函数密切相关
废话少说,see my code:
kernel.c#include "kernel.h"
#include <stdio.h>void shape_draw(struct shape_t* obj) {/* Call draw of the real Instance */obj->vtable->draw();}
kernel.h:#ifndef UNTITLED_KERNEL_H
#define UNTITLED_KERNEL_Hstruct shape_t {/*Virtual Method Table */const struct shape_interface* const vtable;
};struct shape_interface {void (*draw)();};void shape_draw(struct shape_t* obj);//obj->vtable->draw();#endif //UNTITLED_KERNEL_H
try.c:#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "try.h"void draw() {printf("error is try!\n");
}void draw1() {printf("error is try2!\n");
}struct shape_t* shape_create_rectangle() {//直接赋值,这里有个命名错误,跟gcc有关://static const struct shape_interface_t vtable = { draw1 } ;//static struct shape_t base = { &vtable };//笔者认为给结构体成员赋值,下面的写法更妥当static const struct shape_interface vtable = { .draw = draw1} ;static struct shape_t base = { .vtable = &vtable};//推荐上面这种写法,因为某些编译器很有趣struct rectangle_t* rectangle = malloc(sizeof(*rectangle));memcpy(&rectangle->base, &base, sizeof(base));return (struct shape_t*)(&rectangle->base);
}
顺便一提,clion的编译器相当有趣
笔者在之前曾经写错了shape_interface (_t)结构体名称,但是笔者发现:
//static const struct shape_interface_t *vtable = { draw1 } ;
//static struct shape_t base = { &vtable };
改成这样也能运行
这是为什么呢?笔者推测,gcc应该是无法找到对于结构体,就把vtable当成了数组,加上*就成为了数组。然后&vtable就成为了二级指针,由于draw1本身就是一个指针,把它转成空指针什么的可以随便赋值。gcc在找不到对应结构体后,索性为base里的vtable开辟了一段空间,由于&vatble是二级指针,但是找不到对应地址指向,可能它在编译过程中被转为了一级空指针,且等于draw1本身,这样就能解释通了。(如果有c语言高手可以留言解答一下,笔者对c语言和编译器的处理所知甚少)
try.h:#ifndef UNTITLED_TRY_H
#define UNTITLED_TRY_H#include "kernel.h"struct rectangle_t {struct shape_t* base; /* Reference to Base Class *//* Rectangle specific Members */int x;int y;
};struct shape_t* shape_create_rectangle();#endif //UNTITLED_TRY_H
在主函数中这样调用即可:
main.c:#include "try.h"
#include "kernel.h"int main() {struct shape_t* rectangle = shape_create_rectangle();shape_draw(rectangle);return 0;
}
接下来是重点,虚函数表的实现,可以适当改动try.c文件:
try.c://
// Created by el on 2024/8/16.
//#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "try.h"void draw() {printf("error is try!\n");
}void draw1() {printf("error is try2!\n");
}struct shape_t* shape_create_rectangle() {//static const struct shape_interface *vtable[] = { draw , draw1 } ;static const struct shape_interface_t *vtable[] = { .vtable = draw} ;static struct shape_t base = { vtable + 1};struct rectangle_t* rectangle = malloc(sizeof(*rectangle));memcpy(&rectangle->base, &base, sizeof(base));return (struct shape_t*)(&rectangle->base);
}
使用函数指针数组,就可以模拟出比较相近的虚函数表。
整个c程序的UML图如下:

其实这张图跟笔者前一篇模拟多态的文章思想是一样的。
相关文章:
c语言杂谈系列:模拟虚函数
从整体来看,笔者的做法与之前的模拟多态十分相似,毕竟c多态的实现与虚函数密切相关 废话少说,see my code: kernel.c#include "kernel.h" #include <stdio.h>void shape_draw(struct shape_t* obj) {/* Call dr…...
短视频推广App不再难!Xinstall来帮忙
在短视频风靡的今天,如何利用这一热门媒介有效推广App,成为了许多推广者关注的焦点。而Xinstall,作为国内专业的App全渠道统计服务商,正是你解决这一难题的得力助手。 首先,Xinstall在数据维度上的优势无可比拟。它能…...
打靶记录13——doubletrouble
靶机: https://www.vulnhub.com/entry/doubletrouble-1,743/ 难度: 中 目标: 取得两台靶机 root 权限 涉及攻击方法: 主机发现端口扫描Web信息收集开源CMS漏洞利用隐写术密码爆破GTFObins提权SQL盲注脏牛提权 学习记录&am…...
awk文本处理工具
awk 是一个强大的文本处理工具,在Shell编程中常用于处理和分析文本数据。它可以按列处理数据,进行模式匹配,生成报告,执行计算等。以下是一些 awk 的主要功能和使用场景: 期待您的关注 美好的观念较美人尤为可爱 目录 …...
计算机毕业设计选题推荐-学院网站系统-Java/Python项目实战
✨作者主页:IT毕设梦工厂✨ 个人简介:曾从事计算机专业培训教学,擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Py…...
Spring模块详解Ⅰ
目录 SpringSpring框架的主要功能模块1. Core Container(核心容器)2. Data Access/Integration(数据访问与集成)3. Web4. AOP (Aspect-Oriented Programming,面向切面编程)5. Instrumentation(工具集&#…...
C语言程序设计-练习篇
山海自有归期,风雨自有相逢。 一 下面代码的结果是什么? int main() { int i 0; for (i 0; i < 10; i) { if (i 5) //此处为赋值,i 5表达式结果为5 printf("%d ", i); //表达式为真&a…...
【Oracle篇】统计信息和动态采样的深度剖析(第一篇,总共六篇)
💫《博主介绍》:✨又是一天没白过,我是奈斯,DBA一名✨ 💫《擅长领域》:✌️擅长Oracle、MySQL、SQLserver、阿里云AnalyticDB for MySQL(分布式数据仓库)、Linux,也在扩展大数据方向的知识面✌️…...
无源互调自动化测试软件应用案例分享:S参数和互调的高效测试
随着产品种类的丰富和市场需求的变化,合肥某电子技术公司意识到,传统的手工测试已无法满足公司持续发展的需要。于是,一场自动化测试转型悄然展开。 一、背景介绍 合肥某电子技术公司成立于2009年,专注于功分器、耦合器、负载器、…...
【6大设计原则】精通设计模式之里氏代换原则:从理论到实践,掌握代码演化的黄金法则
一、引言 1.1 设计模式的必要性 在软件开发的复杂性面前,设计模式提供了一套成熟的解决方案,它们是经过多年实践总结出来的,能够帮助我们应对各种编程难题。设计模式不仅仅是一种编程技巧,更是一种编程哲学,它能够提…...
国内服务器安装Docker提示Failed to connect to download.docker.com port 443的解决方案
解决方案 换国内镜像源。我用的是清华的。https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/ 自己找自己对应的版本。 例如你的Ubuntu系统。就用下列命令 sudo curl -fsSL https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/ubuntu/gpg -o /etc/apt/keyrings/do…...
前端开发攻略---彻底弄懂跨域解决方案
目录 1、浏览器的同源策略 1.1 源 1.2 同源与非同源 1.3 同源请求与非同源请求 2、跨域受到的限制 3、注意点 4、CORS解决Ajax跨域问题 4.1 CORS概述 4.2 CORS解决简单请求跨域 4.3 简单请求与复杂请求 4.4 CORS解决复杂请求跨域 4.5 借助CORS库快速完成配置 5、JS…...
【HeadFirst 设计模式】装饰者模式的C++实现
一、案例背景 Starbuzz是以扩张速度最快而闻名的咖啡连锁店。如果你在街角看到它的店,在对面街上肯定还会看到另一家。因为扩张速度实在太快了,他们准备更新订单系统,以合乎他们的饮料供应要求。他们原先的类设计是这样的…… 购买咖啡时&am…...
大白话解释TCP的三次握手和四次挥手
你好,我是沐爸,欢迎点赞、收藏和关注。个人知乎 TCP的三次握手是浏览器与服务器建立连接的过程,而四次挥手,是两者断开连接的过程。今天把客户端和服务端当做两个人,通过打电话的方式解释连接建立和断开的过程。 TCP…...
asyncua模块实现OPC UA通讯
asyncua是OPCUA的python实现,使用起来非常方便,其github地址是https://github.com/FreeOpcUa/opcua-asyncio UaExpert是OPC UA Client的GUI工具,当编写好server代码后并运行,我们可以使用UaExpert去和server进行通信。UaExpert使…...
RabbitMQ的核心概念
RabbitMQ是一个消息中间件,也是一个生产者消费者模型,负责接收,存储和转发消息。 核心概念 Producer 生产者,是RabbitMQ Server的客户端,向RabbitMQ发送消息。 Consumer 消费者,是RabbitMQ Server的客…...
【vSphere 7/8】深入浅出 vSphere 证书 Ⅰ—— 初识和了解 vSphere证书
目录 摘要1. vSphere 安全证书1.1 vSphere 安全证书的类型和有效期 2. 在 vSphere Client 中初识 vSphere 证书2.1 vCenter 8.0.3 的 vSphere Client 界面2.2 vCenter Server 7.0 Update2 到 vCenter Server 8.0 Update 2 的 vSphere Client 界面2.3 vCenter Server 7.0 到 vCe…...
【云备份】服务端模块-热点管理
文章目录 0.回顾extern1.介绍2.实现思想3.代码测试代码 热点管理总结 0.回顾extern extern cloudBackup::DataManager *_dataManager extern 关键字用于声明一个全局变量或对象,而不定义它。这意味着 _dataManager 是一个指向 cloudBackup::DataManager 类型的指针…...
call apply bind特性及手动实现
call // 原生的call var foo { value: 1 };function bar(...args) {console.log("this", this.value, args); }bar.call(foo)// call 改变了bar的this指向 // bar函数执行了 // 等价于 // var foo { // name: "tengzhu", // sex: "man", …...
pygame开发课程系列(5): 游戏逻辑
第五章 游戏逻辑 在本章中,我们将探讨游戏开发中的核心逻辑,包括碰撞检测、分数系统和游戏状态管理。这些元素不仅是游戏功能的关键,还能显著提升游戏的趣味性和挑战性。 5.1 碰撞检测 碰撞检测是游戏开发中的一个重要方面,它用…...
深入浅出:JavaScript 中的 `window.crypto.getRandomValues()` 方法
深入浅出:JavaScript 中的 window.crypto.getRandomValues() 方法 在现代 Web 开发中,随机数的生成看似简单,却隐藏着许多玄机。无论是生成密码、加密密钥,还是创建安全令牌,随机数的质量直接关系到系统的安全性。Jav…...
剑指offer20_链表中环的入口节点
链表中环的入口节点 给定一个链表,若其中包含环,则输出环的入口节点。 若其中不包含环,则输出null。 数据范围 节点 val 值取值范围 [ 1 , 1000 ] [1,1000] [1,1000]。 节点 val 值各不相同。 链表长度 [ 0 , 500 ] [0,500] [0,500]。 …...
P3 QT项目----记事本(3.8)
3.8 记事本项目总结 项目源码 1.main.cpp #include "widget.h" #include <QApplication> int main(int argc, char *argv[]) {QApplication a(argc, argv);Widget w;w.show();return a.exec(); } 2.widget.cpp #include "widget.h" #include &q…...
Springcloud:Eureka 高可用集群搭建实战(服务注册与发现的底层原理与避坑指南)
引言:为什么 Eureka 依然是存量系统的核心? 尽管 Nacos 等新注册中心崛起,但金融、电力等保守行业仍有大量系统运行在 Eureka 上。理解其高可用设计与自我保护机制,是保障分布式系统稳定的必修课。本文将手把手带你搭建生产级 Eur…...
HBuilderX安装(uni-app和小程序开发)
下载HBuilderX 访问官方网站:https://www.dcloud.io/hbuilderx.html 根据您的操作系统选择合适版本: Windows版(推荐下载标准版) Windows系统安装步骤 运行安装程序: 双击下载的.exe安装文件 如果出现安全提示&…...
C++中string流知识详解和示例
一、概览与类体系 C 提供三种基于内存字符串的流,定义在 <sstream> 中: std::istringstream:输入流,从已有字符串中读取并解析。std::ostringstream:输出流,向内部缓冲区写入内容,最终取…...
3-11单元格区域边界定位(End属性)学习笔记
返回一个Range 对象,只读。该对象代表包含源区域的区域上端下端左端右端的最后一个单元格。等同于按键 End 向上键(End(xlUp))、End向下键(End(xlDown))、End向左键(End(xlToLeft)End向右键(End(xlToRight)) 注意:它移动的位置必须是相连的有内容的单元格…...
OPenCV CUDA模块图像处理-----对图像执行 均值漂移滤波(Mean Shift Filtering)函数meanShiftFiltering()
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 在 GPU 上对图像执行 均值漂移滤波(Mean Shift Filtering),用于图像分割或平滑处理。 该函数将输入图像中的…...
基于IDIG-GAN的小样本电机轴承故障诊断
目录 🔍 核心问题 一、IDIG-GAN模型原理 1. 整体架构 2. 核心创新点 (1) 梯度归一化(Gradient Normalization) (2) 判别器梯度间隙正则化(Discriminator Gradient Gap Regularization) (3) 自注意力机制(Self-Attention) 3. 完整损失函数 二…...
Vue ③-生命周期 || 脚手架
生命周期 思考:什么时候可以发送初始化渲染请求?(越早越好) 什么时候可以开始操作dom?(至少dom得渲染出来) Vue生命周期: 一个Vue实例从 创建 到 销毁 的整个过程。 生命周期四个…...
