news 2026/6/10 10:47:42

C语言笔记8之经验总结

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
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、读写的执行过程

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

【STM32项目】基于STM32的智能水产养殖系统电路(文章末尾附有完整代码)

一.项目背景随着自动化理论的不断发展,自动化概念及其产品已经不断在工农业中得到应用。农业中水产养殖管理是一个要求养殖人员进行繁重的体力劳动的工作。虽然随着现代化工业的发展,市场上不断的出现各种水产养殖的新型设备,但是这并为给养殖…

作者头像 李华
网站建设 2026/6/10 10:42:12

WebGui性能基准测试:与传统Web UI框架的对比分析

WebGui性能基准测试:与传统Web UI框架的对比分析 【免费下载链接】WebGui An example demo of IMGUI (Immediate Mode GUI) on the web. Using only WebGL, GLFW and ImGui. Suitable for being compiled to web assembly (WASM). 项目地址: https://gitcode.com…

作者头像 李华
网站建设 2026/6/10 10:40:18

实战指南:用nanoGPT在5分钟内构建你的第一个语言模型

实战指南:用nanoGPT在5分钟内构建你的第一个语言模型 【免费下载链接】nanoGPT The simplest, fastest repository for training/finetuning medium-sized GPTs. 项目地址: https://gitcode.com/GitHub_Trending/na/nanoGPT 还在为复杂的Transformer架构和庞…

作者头像 李华
网站建设 2026/6/10 10:40:17

Docker-Jellyfin容器化部署最佳实践:Docker Compose配置与参数详解

Docker-Jellyfin容器化部署最佳实践:Docker Compose配置与参数详解 【免费下载链接】docker-jellyfin 项目地址: https://gitcode.com/gh_mirrors/do/docker-jellyfin 在当今数字化时代,搭建个人媒体服务器已成为许多技术爱好者的需求。Docker-J…

作者头像 李华