嵌入式系统编程中常用的回调处理

2023-11-06

        在嵌入式编程中经常看到形如下图所示的一些函数调用或者函数初始化,这种形式的原理,以及在编程过程中能带来什么好处,可以通过下面这篇文章来简单的解释和说明:

 

 函数是C语言的核心概念。主调函数(caller)调用被调函数(callee)是一般的调用关系,如果被调函数(callee)参数包含函数指针,函数指针还可以形成多一层的调用关系,形成第三方函数的调用,专业术语称为回调(callback),通过函数指针参数调用的第三方函数称为回调函数。回调可以让被调函数(这里是指用函数指针做函数参数的函数)的代码更加泛化或抽象,能够简单模拟其它编程语言的委托与反射语法。

1、简单模拟委托

//C语言简单模拟委托
//需要用的指针函数。通过用指针函数作为地址接收函数地址,以达到委托其他函数实现某方法的目的。
#include <stdio.h>
typedef void(* fun)();  //typedef 把void(*)()类型重命名为fun
void func(fun);  // 被调函数
void func_1();  // 回调函数1
void func_2();  // 回调函数2

int main() // 主函数用做主调函数
{
    func(func_1);
    fun f = func_2;
    f();
    func(func_1);
    func(func_2);
    getchar();
    return 0;
}
void func(fun f)  //fun f为地址,fun * f为f指向的地址的量或者其他
{
    printf("func\n");
    if (f != NULL)
    {
        f();
    }
}
void func_1()
{
    printf("func_1\n");
}
void func_2()
{
    printf("func_2\n");
}
/*
func
func_1
func_2
func
func_1
func
func_2
*/
2、简单模拟反射
(1)简单模拟反射
    高级语言的反射机制,简单来说就是可以通过字符串型获取对应的类或者函数。
    下面用C来简单模拟反射:

#include <stdio.h>
#include <string.h>

typedef void (*callback)(void);

typedef struct {
    const char *name;
    callback fn;
}callback_t;

void f0();
void f1();

callback_t callbacks[] = {
    {"cmd0", f0},
    {"cmd1", f1},
};

void f0()   // 回调函数0
{
    printf("cmd0");
}

void f1()  // 回调函数1
{
    printf("cmd1");
}

void do_callback(const char *name)  
{
    size_t i;
    for (i = 0; i < sizeof(callbacks) / sizeof(callbacks[0]); i++) {
        if (!strcmp(callbacks[i].name, name)) {
            callbacks[i].fn();
        }
    }
}

int main()
{
    do_callback("cmd1");
    getchar();
    return 0;
}
(2)利用自定义段
    gcc支持通过使用 __ attribute __ ((section())),将函数、变量放到指定的数据段中。
    也就是说,可以让编译器帮我们完成上例中向数组添加成员的动作。借助此机制,回调函数可以在任意文件声明,不需要修改其他文件。自定义段的起始和结束地址,可以通过变量 __ start_SECTIONNAME 和 __ stop_SECTIONNAME得到例如通过 __ attribute __ ((section("ss"))定义自定义段,其开始地址为 & __ start_ss,结束地址为 & __stop_ss。

// https://www.bejson.com/runcode/c920/
#include <stdio.h>
#define SEC __attribute__((__section__("ss"), aligned(sizeof(void*))))

void func_1 (int a, int b)
{
    printf("%s %d %d\n", __func__, __LINE__, a+b); 
}
void func_2 (int a, int b)
{
    printf("%s %d %d\n", __func__, __LINE__, a*b); 
}

// 编译器会自动提供__start_ss,__stop_ss标志段ss的起止地址
extern size_t __start_ss;
extern size_t __stop_ss;

typedef struct {
    void (*p)(int, int);
} node_t;

// 结构体变量a位于自定义段ss
SEC node_t a = { 
    .p = func_1, 
};
SEC node_t b = { 
    .p = func_2, 
};
int main(int argc, char **argv)
{
   int a = 3, b = 4;
    node_t *p;
    // 遍历段ss,执行node_t结构中的p指向的函数
    for (p = (node_t *)&__start_ss; p < (node_t *)&__stop_ss;p++) {
        p->p(a, b);
        a+=1;b+=2;
    }
}
/*
func_1 6 7
func_2 10 24
*/

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

嵌入式系统编程中常用的回调处理 的相关文章

  • 一文讲解单片机、 ARM、 MCU、 DSP、 FPGA、 嵌入式错综复杂的关系

    概述 一文讲解单片机 ARM MCU DSP FPGA 嵌入式错综复杂的关系 首先 嵌入式 这是个概念 准确的定义没有 各个书上都有各自的定义 但是主要思想是一样的 就是相比较PC机这种通用系统来说 嵌入式系统是个专用系统 结构精简 在硬件
  • STM32 基础系列教程 18 – IWDG

    前言 学习stm32 独立看门狗 IWDG 接口使用 学会用STM32内部独立看门狗 IWDG 实现程序异常时自复位功能 STM32F10xxx内置两个看门狗 提供了更高的安全性 时间的精确性和使用的灵活性 两个看门狗设备 独立看门狗和窗口
  • STM32 基础系列教程 38 - Lwip_http

    前言 HTTP协议 HyperText Transfer Protocol 超文本传输协议 是因特网上应用最为广泛的种网络传输协议 所有的WWW文件都必须遵守这个标准 HTTP是一个基于TCP IP通信协议来传递数据 HTML 文件 图片文
  • 《深入理解计算机系统》(CSAPP)实验三 —— Buf Lab

    这是CSAPP的第三个实验 主要让我们熟悉GDB的使用 理解程序栈帧的结构和缓冲区溢出的原理 实验目的 本实验的目的在于加深对IA 32函数调用规则和栈结构的具体理解 实验的主要内容是对一个可执行程序 bufbomb 实施一系列缓冲区溢出攻
  • 12.示例程序(定时器定时中断&定时器外部时钟)

    目录 定时中断和时钟源选择相关库函数使用 1 定时器初始化配置 2 参数 PSC ARR等 更改函数 在程序运行过程中修改 3 使用定时器库函数的一些细节 定时器定时中断实例 定时器外部时钟选择 知识点get 滤波器工作原理 可以滤掉信号的
  • 嵌入式开发之堆栈调试打印

    简介 打印堆栈的常用方法包括 glibc中的backtrace函数 gcc内置函数 builtin return address 第三方库libunwind 1 glibc中的backtrace 1 1函数原型 include
  • 利用三轴加速度求解位移的算法—来自飞思卡尔方案

    在要求精度不高的情况 可以使用三轴加速度积分得到位移 飞思卡尔给出了官方方法 下文来自翻译说明 cache freescale com files senso 摘要 此文档描述并使用MMA7260QT三轴加速计和低功耗的9S08QG8八位单
  • stm32+DS1302+TM1638驱动程序

    TM1638数码管显示驱动程序 参考 1 TM1638与STM32连接 1 1 硬件连接 Vcc 电源 GND 电源地 STB PA0 CLK PA1 DIO PA2 1 2 驱动程序 TM1638 c文件 Program Assignme
  • Unicode 编码表下载

    概述 很多项目都使用了Unicode 编码表 在此 做个笔录 官网 1 第一入口 https home unicode org 2 第二入口 Unicode 14 0 Character Code Charts 3 第三入口 http ww
  • 交叉编译tslib (正确版)

    1 下载安装交叉编译器 编译器版本不限 需要与系统移植时的编译器保持一致即可 2 下载tslib1 4 3 交叉编译tslib 1 4 下载的tslib 1 4 tar gz放到 home driver ts 目录下 cd home dri
  • 基于stm32f107 stm32cube 和 LWIP 协议实现 udp 组播通信

    最近在做一个基于stm32f107 实现 UDP 组播通信的项目 项目基于 stm32cube 配置生成 如下图 UDP组播头文件 ifndef MULTICAST H define MULTICAST H include lwip udp
  • STM32 基础系列教程 31 – DAC

    前言 学习stm32 DAC数模转换接口使用 学会用STM32 的DAC接口 通过DAC接口将数字信号转变成模拟信号输出 或查看内部变量值的变化波形 学习DAC波形发生器的使用 示例详解 基于硬件平台 NUCLEO F302R8 NUCLE
  • 线性回归方程

    线性回归方程在嵌入式开发中是非常常用的 尤其在参数校准这块应用非常普遍 无论你是写在上位机代码中 还是直接写在嵌入式软件中 下面是我在PT100校准中写的关于线性回归方程代码 线性回归方程公式 平均值XA X1 X2 XN N 平均值YA
  • 基于MCU,如何零代码无开发实现OTA差分升级?

    概述 随着物联网设备智能化的发展 OTA 升级已经成为了业界公认的基础能力 而 OTA 在设计和实现过程中需要依赖于物联网设备的硬件方案 物联网设备主流的硬件方案为 1 直接基于通讯芯片 模块开发的方案 用户升级的主要目标是通讯芯片 模块
  • 嵌入式软件国际化(多语言) 点阵字库选择分析

    概述 嵌入式软件国际化 多语言 点阵字库选择分析 多字节字符集与unicode 多字节编码最大的问题在于每一个语种的字符集编码不兼容 unicode的好处在于将所有人类语种字符都有了统一的编码 现在世界上语言基本都包含在了基本多文种平面0上
  • printk函数的用法

    printk在内核源码中用来记录日志信息的函数 只能在内核源码范围内使用 用法和printf非常相似 printk函数主要做两件事情 第一件就是将信息记录到log中 而第二件事就是调用控制台驱动来将信息输出 1 日志级别 printk相比p
  • 稳压二极管工作原理、重要参数意义和典型电路参数计算

    稳压二极管的工作原理 稳压二极管也叫稳压管 它在电路中一般起到稳定电压的作用 也可以为电路提供基准电压值 稳压二极管使用特殊工艺制造 这种工艺使它在反向击穿时仍然可以长时间稳定工作 不损坏 而工作在反向击穿状态的稳压管只要工作电流保持在一定
  • SEGGER_RTT_printf()函数实现打印浮点、负数-示例

    概述 最近公司项目换另一款gsensor 用到了浮点数打印 又不想使用串口来打印数据 在此做个笔录 通过修改源码方式实现 一 修改源码 1 在 SEGGER RTT printf c 中 的 int SEGGER RTT vprintf u
  • 在WINDOW 系统下如何用批处理命令生成代码

    如图要实现一个每次编译都会自动重新生成的代码 一般是 软件版本相关的代码最适合这种自动生成 上图中需在用到编译时间和日期 来直接上代码 BEGIN COLOR 07 cls echo off ECHO ECHO 自动生成软件版本号 ECHO
  • GPIO引脚的模式设置:开漏、推挽、拉高、拉低、中断输入、串行通信、模拟输入输出、容错输入、PWM输出。过零检测介绍。

    开漏输出 软件 将GPIO口设置为开漏输出模式 可以实现开漏输出控制方式 输出电平只能被拉低 而不能被拉高 在使用开漏输出时 需要外部接上一个上拉电阻 将输出电平拉高到高电平 开漏输出常用于驱动I2C总线 LED灯等场景中 硬件设置为开漏输

随机推荐

  • mysql char 和varchar的区别?

    char 和varchar的区别 1 char 一定会使用指定的空间 varchar是根据数据来定空间 2 char的插入数据效率理论上比varchar高 varchar是需要通过后面的记录数来计算 使用哪一种类型 如果确定数据一定是占指定
  • C++桌面端使用 zxing-cpp 和 opencv 生成二维码(带中间logo)

    一 环境工具准备 VS2019 Cmake zxing cpp 源代码 https gitee com asalmc zxing cpp opencv4 2 0 二 zxing cpp 编译 1 源码目录下新建build文件夹存放构建目录
  • EC20 配置DHCP客户端自动获取IP

    1 dnsmasq介绍 Dnsmasq为小型网络提供网络基础设施 DNS DHCP 路由器通告和网络引导 它被设计为轻量级且占用空间小 适用于资源受限的路由器和防火墙 它还被广泛用于智能手机和便携式热点的共享 并支持虚拟化框架中的虚拟网络
  • 特征训练、预测一致性管理工具:开源项目Feast

    在机器学习的流程大体可以分成模型训练和模型服务两个阶段 无论是训练和服务阶段 其实都需要进行特征工程相关的工作 这块的技术挑战就是如何保证训练和预测过程中使用的特征是一致的 这个问题困扰了很多机器学习从业者 比较典型的场景就是推荐场景 在推
  • phpstudy(小皮模板存在nginx解析漏洞)

    前言 好久没写文章了 最近比较忙 今天抽个空写点东西 phpstudy介绍 PhpStudy国内12年老牌公益软件 集安全 高效 功能与一体 已获得全球用户认可安装 运维也高效 支持一键LAMP LNMP 集群 监控 网站 数据库 FTP
  • IAR 软件激活步骤

    1 下载IAR软件 然后点击安装 一路NEXT 2 安装后激活 需要断网 3 打开IAR软件 在软件目录中点击help License manger 4 弹出IAR License manager界面 点击license offline a
  • 阿里云商标注册入口/查询/买卖/分类表/撤三/续展/驳回复审入口汇总一键直达

    阿里云商标服务包括商标注册申请 商标近似查询 商标买卖 商标分类表 商标撤三申请 商标续展申请 商标驳回复审 商标起名及商标管理后台等服务 阿里云百科汇总阿里云商标服务入口大全 本文长期更新阿里云商标注册各种链接地址 阿里云商标服务入口链接
  • matplotlib学习

    figure axes axis add subplot subplot figure包括axes figure是画板 axes是画板上的子图 figure 使用add subplot pyplot使用的是subplot生成一个figure
  • (理财八)普通必须掌握的理财方式----定投

    理财八 普通必须掌握的理财方式 定投 我们先要了解一种 一直会用到的 打理股权类产品的投资方式一一基金定投 基金定投不是一个产品 是打理账户用的方法 是未来讲到股票基金配置时候 买入频率的方法 这种方法是被验证确实能帮小白理财者挣到钱的方式
  • 计算机网络01之计算机网络分层结构

    计算机网络01 1 计算机网络分层结构 1 下层为上层提供服务 SDU数据单元 为完成用户所要求功能而应传送的数据 PCI协议控制单元 控制协议操作的信息 PDU协议数据单元 对等层次之间传送的数据单位 2 OSI 7层参考模型 OSI 7
  • 微分中值定理定义及几何意义

    微分中值定理定义及几何意义 1 罗尔定理 2 拉格朗日中值定理 3柯西中值定理 1 罗尔定理 如果函数f x 满足 1 在闭区间 a b 上连续 2 在开区间 a b 内可导 3 f a f b 则在 a b 内至少有一点 a b 使得f
  • QT信号和槽

    系列文章目录 提示 这里可以添加系列文章的所有文章的目录 目录需要自己手动添加 例如 第一章 Python 机器学习入门之pandas的使用 提示 写完文章后 目录可以自动生成 如何生成可参考右边的帮助文档 文章目录 系列文章目录 前言 一
  • Node.js后端开发 - 基础篇 #2 全局对象

    文章目录 一 前言 二 全局对象 1 console打印输出 2 setTimeout超时输出 3 setInterval循环间隔输出 4 clearInterval 清除循环间隔输出 5 dirname输出当前所在目录 6 filenam
  • 周志华 机器学习 Day26

    学习与推断 基于概率图模型定义的联合概率分布 我们能对目标变量的边际分布或以某些可观测变量为条件的条件分布进行推断 边际分布是指对无关变量求和或积分后得到的结果 例如在马尔可夫网中 变量的联合分布呗表示成极大团的势函数乘积 于是 给定参数
  • 华为OD机试 - 不含101的数(Java)

    题目描述 小明在学习二进制时 发现了一类不含 101的数 也就是 将数字用二进制表示 不能出现 101 现在给定一个整数区间 l r 请问这个区间包含了多少个不含 101 的数 输入描述 输入的唯一一行包含两个正整数 l r 1 l r 1
  • NodeMcu arduino ESP8266 使用WIFIManager 库

    WiFiManager库使用说明 提示 这里可以添加系列文章的所有文章的目录 目录需要自己手动添加 例如 第一章 Python 机器学习入门之pandas的使用 提示 写完文章后 目录可以自动生成 如何生成可参考右边的帮助文档 文章目录 W
  • matlab作出马鞍面,[2018年最新整理]实验一马鞍面绘制实验.ppt

    2018年最新整理 实验一马鞍面绘制实验 10 实验一 马鞍面绘图实验 实验目的和实验内容 实验原理 实验相关的思考问题 熟悉几个函数 mesh contour linspace figure 显示图形框 mesh X Y Z Meshz
  • 项目上线后遇到的问题总结

    项目上线了 一堆堆的问题也随之出现了 除了时间比较匆忙导致没有细致的做验证之外 当初也确实没有在最重要的功能需求上把好关 导致后来要做很多的修改而弥补之前的错误 下面是上线后遇到的问题和解决办法总结 问题一 用户非正常流程导致的错误 用户注
  • 【读书笔记->统计学】07-03 离散型概率分布-泊松分布概念简介

    泊松分布 假设一个情境 下星期电影院有一个大型促销 影院经理希望一切都完美无缺 爆米花机每一周的平均故障次数为3 4 或者说爆米花机的故障率为3 4 求爆米花机下一周不发生故障的概率有多大 如果预计故障太多次 就打算买个新的爆米花机了 与前
  • 嵌入式系统编程中常用的回调处理

    在嵌入式编程中经常看到形如下图所示的一些函数调用或者函数初始化 这种形式的原理 以及在编程过程中能带来什么好处 可以通过下面这篇文章来简单的解释和说明 函数是C语言的核心概念 主调函数 caller 调用被调函数 callee 是一般的调用