当前位置: 首页 > news >正文

C语言笔记8之经验总结

说明

以下是出道 two years 的经验笔记,持续更新

笔记内容

tip1

当一个结构体变量涉及到flash 读写通信收发(按照特定字节格式的协议)等,其数据内容不能受到默认结构体对齐填补后的影响时,需要加上 __attribute__((packed)),让其按照字节的格式对齐

tip2

解析接收数据帧的常规思路

1、先从接收缓冲区找到完整数据帧

2、通过强制类型转换获取数据帧

3、注意这里指针类型强转后,Protal_st *p = (Protal_st *)pData,假设pData执行buff[0],实际上p->data 的值是 buff[3]、buff[4]、buff[5]、buff[6]组合的一个int值赋给的,所以这里需要重新指定

p->data = (pData + offsetof(Protal_st, data)),即p->data = &buff[3]

4、注意一般来说单片机都是小端模式的,所以报文通信也必须是小端模式,即低字节在前,否则 这里的 uint16_t len = p->len 还需要高低字节互换

tip3

注意函数的参数副本概念,上一篇已经说过了

tip4

当传递函数名 myFunction 时,两种方式传 &myFunction 或 myFunction 都行,都是指传递函数名的地址

tip5

注意不要混淆,联合体和枚举一样的位置

当struct 时,上边是类型名,下边是变量

当typedef struct时,下边是类型名,其不能直接定义变量,以为typedef 是声明作用,声明一个别名

tip6

注意以下两种在链表中定义节点的方式,第一种是错误的

我的理解方式

总的来说就是,当第一种typedef 方式时,编译器在 编译 ListNote *preNote 这行时,它找不到ListNote 这个关键字,因为下一行这个 ListNote 关键字才出现;当第二种 struct 方式时,编译器在 编译 struct ListNote *preNote时,它在第一行就已经找到 struct ListNote的关键字了,即使此时struct ListNote 依旧是未完全定义,但编译是通过的

因此,再看下面两种方式,就很容易理解了

tip7

结构体成员的访问和最终表示,总体来说最终表示的是数组/变量/指针就看最后一个成员变量是什么类型的数据

tip8

注意 sizeof 宏定义的使用,sizeof 变量名、数组名、类型名返回的是占用的字节数

sizeof 地址、指针返回的是4个字节,即 int 类型

tip9

注意结构体类型,当不加 __attribute__((packed)),即按照编译器默认结构体对齐,也就是说当你使用这个结构体变量读到内存中时,编译器会自动补齐,即sizeof(结构体类型/变量) >= 成员变量占用字节大小的累积和

其实,结构体类型的对齐方式是编译阶段就已经是确定了,其需填充字节数也确定了

假如上述结构体类型在编译阶段按照 uint32_t 类型对齐,对齐结果如下,通常补齐的字节都是0x00

我的理解

由于结构体成员地址统一对齐后,其成员的访问方式通过地址偏移操作,大大提升了CPU的访问结构体成员的效率;当成员未对齐,即按照字节的偏移方式访问成员,一个字节一个字节的查询,故效率低下

tip10

取消结构体的默认对齐方式,通常两种

1、使用__attribute__((__packed__)),针对某个结构体,且只能按照1个字节对齐

2、使用宏,针对多个结构体,且能按照1、2、4、8等字节对齐

tip11

inline函数,编译阶段在函数调用的地方,将函数内容展开,后续将不需要执行函数调用的开销,缺点是代码量增大

所以用法:函数内容少,一般几行的函数,可以使用内联函数,较少函数调用的开销,提高性能

tip12 结构体和联合体

1、联合体的初始化

2、联合体的赋值

3、结构体的初始化

4、结构体的赋值

tip13、函数指针

1、普通声明,p是一个函数指针,指向的函数类型是 func

2、函数指针类型声明,cert_handler_op 是一种函数指针类型,指向 func 这种类型的函数。op 是函数指针,func 函数名本身就代表该函数的地址,故可直接赋值。

tip14、数组指针

tip15、指针数组

tip16、void * 通用指针

void 表示无类型,函数定义时表示无返回值、无参数,此时不需要加return,如下

void *p = NULL; p 是一个通用指针

tip17、回调函数

1、概念

回调函数本质上就是函数指针,那使用回调函数的时机是什么?首先理解两个角色,以及其负责的工作

1、回调函数提供者:负责提供回调函数,管控具体的回调函数的内部执行内容,但不管控回调函数的执行时机、具体的参数传递

2、回调函数执行者:负责在适当的时机执行回调函数,传递具体的参数,不关心回调函数内部的实现

接下来,跟着应用场景去理解回调函数的使用时机、优势

2、应用场景

1、事件驱动

假设我们在应用层的模块中,此时程序执行到某个时机,需要执行驱动层的继电器闭合操作,通常我们是在代码的执行地方,调用驱动层的函数接口,如下图

那问题来了,如果我驱动层代码换了,继电器闭合的接口函数改名了,那我需要替换上述应用层调用的接口函数。也就是说,驱动层改了,应用层也需要跟着改,没法实现应用层和驱动层的解耦

1、应用层头文件

假设如图是回调函数类型、应用层上下文的结构体

2、应用层上下文实例

3、应用层注册函数

4、驱动层具体的继电器操作函数

驱动层:不管控回调函数的执行时机、具体的参数传递只负责具体的回调函数的实现

5、应用层运行前,先注册好驱动层的继电器操作的回调函数;若驱动层的继电器操作函数变了,应用层只需替换这里:contactorA_Opt ——> contactorB_Opt

6、应用层执行继电器闭合的地方

应用层:不关心具体的回调函数的实现,只管控回调函数的执行时机、具体的参数传递

7、结论:因此,应用层和驱动层的唯一耦合在 register_event() 函数,解耦完成!

2、异步通知

假设有两个状态机A、B,主状态机A 负责整机流程,B则负责从FTP服务器下载文件。当B下载完成后,一般都通过变量标志位Flag 或 事件组标志位。当然,也可以通过回调函数,如图

1、A、B上下文

2、A、B上下文实例

3、B 提供注册函数

4、A 提供具体的通知函数

状态机A:不管控回调函数的执行时机、具体的参数传递只负责具体的回调函数的实现

5、A 在 B 去下载文件前,注册下载完成通知函数

6、A状态机中,等待 B下载完成,waiting.....

7、B状态机中,下载完成,执行通知回调函数,通知A下载完成

状态机B:不关心具体的回调函数的实现,只管控回调函数的执行时机、具体的参数传递

8、结论:虽然解耦了,但似乎把代码复杂化了,若使用事件组标志位,一两句就搞定了,无需这么多麻烦的封装。因此,合理选择使用回调函数解耦的时机,很重要!!!

3、状态机之间数据传递

1、假设有两个状态机A、B,上下文如下;场景是A可以设置B的value,B可以获取A的status

2、A、B的上下文实例

3、A提供具体的获取status函数

4、B提供具体的设置value函数

5、A的注册回调函数

6、B的注册回调函数

7、A中设置B的value

8、B中获取A的status

4、读写Buff

假设应用层在某个事件下,需要从SD/外部Flash/模块收发缓冲区/.....读or写入一串数据。

1、上下文如下

2、上下文实例

3、驱动层提供具体的读写函数

4、读写注册函数

5、读写的执行过程

http://www.jsqmd.com/news/985243/

相关文章:

  • 保姆级教程:用STM32CubeMX V6.1.0给STM32H743II配置400MHz主频(从HSE到PLL全流程)
  • 2026 菏泽厨卫屋面地下室漏水瓷砖空鼓测评:吉修匠 99.8 分五星榜首 - 吉修匠
  • git 命令汇总
  • 从分布式到SOA:聊聊汽车OTA技术架构的演变与选型实战
  • 终极指南:如何用De-Bloater快速清理Android系统垃圾应用
  • 突破性轻量化方案:零门槛实现AI数字人创作
  • ADS 2024 实战:手把手教你用负载牵引优化功放效率(从72%到78%)
  • 终极社交媒体数据解决方案:TikHub API Python SDK 完整指南
  • 从汽车到无人机:手把手教你为STM32C8T6的CAN接口配置不同场景的波特率
  • 遗传算法实战进阶:种群动力学、自适应调控与工程化落地
  • 2026年郑州石英石板材采购指南:源头工厂vs代理商,一手货源怎么选才不踩坑? - 年度推荐企业名录
  • Wan2.2提示词扩展技术:从新手到专业导演的AI视频创作指南
  • 小金锁金吊坠闲置,长沙合扬黄金变现小件也能高价回收 - 奢侈品交易观察员
  • 南京高考复读机构哪家好?2026 综合实力排名完整榜单 - 速递信息
  • PowerToys战略应用深度解析:企业级生产力赋能实战指南
  • 智汇客源联系方式,全域流量时代,谁能真正解决门店拓客难题 - GrowthUME
  • react19【系列实用教程】useReducer(含 useImmerReducer ) —— 升级版的 useState (2026最新版)
  • 单片机毕业设计——基于单片机的太阳能充电照明系统 要怎么设计与实现呢(全程可免费指导)
  • 2026济南黄金回收避坑指南!别再被扣费套路骗钱,内行都选这家正规店 - 奢侈品回收评测
  • 特斯拉行车记录仪视频合并终极指南:一键整合6路摄像头,轻松制作专业行车视频
  • 实测!用NCNN在安卓上跑YOLOv5目标检测,性能优化与内存占用全解析
  • Path of Building PoE2:流放之路2角色构建的终极规划指南
  • 2026免费更换背景软件保姆级教程,手机电脑多款工具手把手教你用 - 办公小帮手
  • 鸡肉调理腌料生产厂家常见问题解答 - 速递信息
  • 从‘装箱问题’到快递打包:用C++模拟优化你的包裹空间(附完整代码)
  • KoboldCPP:如何在Android手机上搭建你的私有AI助手?
  • 如何在GTA5中构建终极安全防护:YimMenu完整使用指南
  • Open UI5 源代码解析之1440:CompVariantSaveAs.js
  • 2026年6月网站制作工具横评:八大产品价格、功能与服务对比 - 比文云BBWEYY餐宝盈
  • 如何在Sublime Text中安装sublime-phpcs?5分钟快速上手教程