凡亿教育-婷婷
凡事用心,一起进步
打开APP
公司名片
凡亿专栏 | malloc,真的不适合单片机
malloc,真的不适合单片机

雷猴啊~我是无际。

          

 

今天我们要聊一个听起来有点“玄学”但实际上很接地气的话题:为什么malloc不适合用于单片机编程,而在PC中却铺天盖地。

1. malloc 是谁?是干嘛的?

malloc 是 C 语言里的一个函数,全名叫 “memory allocation”,翻译过来就是“内存分配”。

          

 

它的任务很简单——从内存的“堆”(heap)里划出一块地盘给你用,告诉你地址(返回一个指针),然后你就可以在这块地盘上随便折腾。需要多大?告诉它字节数就行。用完了怎么办?调用 free 还回去,有借有还。

          
在PC用得很多,而在单片机,很少工程师会用。

          

 

2. PC 和单片机的资源差别

          

 

要搞清楚 malloc 的差异,咱们得先看看 PC 和单片机的资源有多不一样。

          

 

2.1 内存

•PC:随便一台现代 PC,动不动就是 8GB、16GB 的 RAM,内存多到可以用“土豪”来形容。更别提它还有虚拟内存这张王牌,RAM 不够用时可以借硬盘的存储空间来凑,简直是“有钱任性”。

          

 

    

•单片机:再看看单片机,内存就寒酸多了。8 位单片机可能只有几 KB 的 RAM,32 位的 ARM Cortex-M 系列稍微好点,也就几十 KB 到几百 KB。这样的内存量,连 PC 的一个零头都不到,程序员得像管家婆一样精打细算。

          

 

2.2 处理器

•PC:PC 的处理器是个多面手,支持多任务操作系统、虚拟内存、内存保护这些高级功能。内存管理有操作系统和硬件撑腰,程序员可以放心大胆地把它玩出花。

          

 

•单片机:单片机的处理器就朴实多了,通常是精简架构,大多数时候跑的是“裸机”程序(没操作系统),或者简单的实时操作系统(RTOS)。没有虚拟内存,也没有内存管理单元(MMU),内存怎么用全靠程序员自己操心。

          

 

2.3 用途

          

 

•PC:PC 要干的事五花八门——上网、打游戏、写代码、处理图片,内存需求千变万化,动态分配是刚需。

          

 

•单片机:单片机多半在嵌入式系统里干活,比如控制洗衣机、采集传感器数据、控制电机啥的。它的工作模式相对固定,实时性、稳定性比灵活性更重要。

          

 

硬件的这些差异,就像给 PC 和单片机画了两条完全不同的起跑线,直接影响了 malloc 的“上场时间”。

          

 

3. PC 里 malloc 的黄金时代    

          

 

在 PC 编程中,malloc 为什么这么受欢迎?原因有这么几个。

          

 

3.1 内存需求不确定

写 PC 程序时,内存需求常常是“薛定谔的猫”——编译时根本不知道要用多少。

          

 

比如,你写个文本编辑器,用户可能打开一个 1KB 的小文件,也可能丢给你一个 10MB 的巨无霸日志文件。

          

 

图像处理软件也是,图片分辨率不同,内存需求天差地别。malloc 就像个救场神器,运行时动态分配,完美应对这种不确定性。

          

 

3.2 面向对象编程

          

 

在 C   这类语言里,对象通常是运行时创建的,比如 new 一个类实例。别看表面是 new,底层还是老老实实调用 malloc。动态内存分配让程序可以随时创建对象、销毁对象,代码模块化得像搭积木,维护起来也方便。

          

 

3.3 库和框架的“幕后推手”

PC 编程有各种现成的库和框架,比如 C   的 STL(标准模板库)。你用 vector、list 这些容器时,内部就是靠 malloc 在分配内存。程序员可能都没直接写 malloc,但用这些工具时,已经间接成了它的“常客”。

          

 

    

3.4 单片机里 malloc 的“冷板凳”

          

 

反观单片机,malloc 却成了“冷板凳选手”,甚至有些项目直接把它拉进黑名单。为什么呢?答案有点扎心。

          

 

3.5 内存碎片:工程师的噩梦

          

 

malloc 分配和释放内存时,可能会搞出“内存碎片”。

          

 

啥叫碎片?就是内存里散落着一堆小块空闲空间,加起来可能有几 KB,但没一块是连续的。

          

 

结果呢?你想分配一个大点的内存块时,即使空闲内存总数够用,也因为没有连续空间而失败。

          

 

PC 不怕这问题,内存多得像大海,碎片化了还能靠虚拟内存和操作系统收拾残局。单片机可不行,RAM 就那么点地方,碎片化一次,可能就直接“Game Over”。

          

 

举个例子:假设单片机有 32KB RAM,程序跑着跑着分配释放了几次内存,留下一堆零散的小块空间。

          

 

后来需要 1KB 的连续内存,结果发现最大连续块只有 800 字节——分配失败,程序崩溃。这在单片机里可不是小事。

          

 

而且出现这种引发的死机现象是随机的,可能小批量测不出来,一出货遍地开花出问题,那酸爽,我已经不敢再想下去了。    

          

 

3.6 实时性

单片机应用常常要“争分夺秒”。比如汽车 ABS 系统,必须在几毫秒内响应;

          

 

工业控制里的 PID 算法,也得按时算完。

          

 

malloc 的问题在于,它分配内存的时间不确定——碎片多的时候,可能要花更长时间找合适的空间,在实时性要求高的场景里,是致命的。

          

 

3.7 资源紧张

单片机的 RAM 和 Flash 都少得可怜,每一字节都得掰开揉碎了用。

          

 

malloc 本身是有开销的,比如内存管理需要额外的元数据(记录分配块的大小、状态等),还有函数调用的时间成本。在单片机这块“贫瘠的土地”上,这些开销就像雪上加霜。

          

 

3.8 稳定性:不能赌的“命根子”

嵌入式系统里,稳定性是头等大事。程序崩了,可能不是重启浏览器那么简单——洗衣机罢工、传感器失灵,甚至汽车刹车出问题,说出来,老板分分钟都被你吓尿,公司分分钟赔到破产。

          

 

malloc 增加了风险,比如内存泄漏(忘了 free)、野指针(释放后还用),这些 bug 在单片机里可能直接“炸锅”。相比之下,静态分配内存(编译时就定好)更可控,程序员心里有底。    

          

 

4. 我非要在单片机中用内存管理咋办?

          

 

既然 malloc 不香,那单片机程序员是怎么管内存的?答案是:靠静态分配、栈分配和内存池这些“绝技”。

          

 

4.1 静态分配:提前规划好

          

 

单片机里,大部分内存需求是在写代码时就敲定的。比如:


uint8_t buffer[256];  // 静态分配一个 256 字节的缓冲区


这块内存从程序启动到结束都占着,很LOW,但简单粗暴不会出问题,坏处是灵活性为零。

          

 

4.2 栈分配:用完就扔

          

 

对于短命的内存需求,单片机用栈来解决。比如:






void function() {    uint8_t temp[10];  // 栈上分配 10 字节    // 用完自动释放}

          

 

栈的好处是快,函数结束就自动清理,不用操心释放。但栈空间通常很小(几百字节到几 KB),塞不下大块内存。

          

 

4.3 内存池动态分配

          

 

有些场景需要动态分配,但又不想用 malloc,怎么办?内存池来救场。

          

 

比如我们无际单片机特训营的报警网关项目,有配对探测器的功能,默认是0,但是不同的用户,可能配对的探测器数量不一样,比如有些要门磁、红外、烟感、遥控器,有些则只需要遥控器和门磁。

          

 

我们在菜单显示已配对的探测器时,就头痛了,那到底要分配多少的探测器存储空间?

          

 

以前我们是采用最LOW,最笨的办法,比如主机支持100个探测器,我们就直接分配100个探测器的存储空间,坏处是浪费RAM。    

37a6c7c7e6910e7d4778bda1f54c9d.jpg

          

 

所以,这个时候,动态分配内存就能很好地解决这个问题了。

          

 

动态内存分配,就是先给内存池就是预先划好一块内存,切成固定大小的小块,程序需要时拿一块,用完还回去。比如:




























#define POOL_SIZE 10#define BLOCK_SIZE 16uint8_t memory_pool[POOL_SIZE][BLOCK_SIZE];  // 10 个 16 字节的块bool pool_used[POOL_SIZE] = {0};              // 标记哪些块在用void* pool_alloc() {    for (int i = 0; i < POOL_SIZE; i  )     {        if (!pool_used[i])         {            pool_used[i] = true;            return memory_pool[i];        }    }    return NULL;  // 没空块了}void pool_free(void* ptr) {    for (int i = 0; i < POOL_SIZE; i  )     {        if (memory_pool[i] == ptr)         {            pool_used[i] = false;            return;        }    }}

内存池避免了碎片化(块大小固定),分配释放也快,特别适合单片机这种“精打细算”的环境。

          

 

以上只是一个简易伪代码模型,实际上,动态内存分配涉及数据结构和算法,远远没这么简单。

          

 

我自己改了一个适合单片机的内存管理代码,包含动态分配和释放。

          

 

分配:    

a3eed511a8be3d967181c306365e8a.jpg

          

 

释放:

ca19b0a9dd92eba41fd73f695ad288.jpg

          

 

由于文章篇幅有限,我已上传到资料包,可找我拿。

          

 

5. malloc 在单片机里也有“春天”?

          

 

虽然 malloc 在单片机里不受待见,但它也不是完全没用武之地,甚至很多时候是刚需,比如我上面说我们报警网关的例子,类似的还有很多。

          

 

    

比如我们自己写的架构,如果用上动态内存分配,会灵活很多。

7e378a8931454aa2264cb0be9a469b.jpg

          

 

还有以下场景,也可以适当使用malloc。 

          

 

5.1 内存多

          

 

现在有些高端单片机,比如 STM32H7,RAM 有 1MB,资源不再那么捉襟见肘。这种情况下,适度用 malloc 是可以接受的。不过碎片化和实时性问题还是得小心。

          

 

5.2  RTOS 的加持

          

 

跑实时操作系统的单片机,比如用 FreeRTOS,系统会提供类似 pvPortMalloc 和 vPortFree 的函数。这些是 malloc 的“定制版”,可以用,但建议别太频繁,免得自找麻烦。

          

 

5.3 调试时

开发阶段,malloc 可以当“临时工”,帮你快速验证代码逻辑。等到了产品化阶段,还是老老实实换成静态分配或内存池,稳妥第一。    

          

 

6. malloc 和 free 的“坑”

          

 

别以为 malloc 只在单片机里麻烦,在 PC 上用它也得小心翼翼,不然一堆坑等着你。在单片机里,这些坑的后果更严重。

          

 

•内存泄漏:忘了还债

分配了内存没 free,就像借钱不还。PC 上内存多,漏点无所谓;单片机里漏几 KB,可能直接把系统拖垮。

          

 

•野指针

释放内存后忘了把指针设为 NULL,再用它就像撞鬼,程序可能崩得莫名其妙。

          

 

•重复释放

          

 

一块内存 free 两次,内存管理系统会懵圈,可能直接宕机。

          

 

•分配失败

          

 

内存不够时,malloc 返回 NULL。不检查就用,空指针解引用,程序秒崩。

          

 

这些坑在单片机里更容易踩中,程序员得打起十二分精神。

          

 

    

7. 两种编程的“灵魂差异”

          

 

最后,咱们聊聊 PC 和单片机编程在哲学上的区别。

          

 

7.1 PC 编程:灵活至上

          

 

PC 程序追求灵活性、通用性,代码要好维护、能复用。malloc 这种动态工具,简直是为这种理念量身打造的。

          

 

7.2 单片机编程:稳字当头

单片机更看重效率和稳定性。程序员得跟硬件亲密接触,每一行代码都要精雕细琢,确保在资源有限的情况下不出岔子。静态分配和内存池这种“稳扎稳打”的方式,才是单片机的灵魂。

          

 

          

 

说了这么多,malloc 在 PC 里是大明星,在单片机里是冷门角色,根源在于两者的硬件条件和应用需求不同。PC 内存多、任务杂,malloc 的灵活性如鱼得水;单片机资源紧、要求高,静态分配和内存池更靠谱。

          

 

但这不是说 malloc 在单片机里一无是处,关键看场景和需求。程序员的智慧就在于:根据硬件和任务,挑对工具,而不是盲目跟风。

          

 

希望这篇能让你对 malloc 的“两面人生”有更深的理解。

          

 

    

以后写代码时,不管是 PC 还是单片机,都能心里有数、手里有招。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表凡亿课堂立场。文章及其配图仅供工程师学习之用,如有内容图片侵权或者其他问题,请联系本站作侵删。
相关阅读
进入分区查看更多精彩内容>
精彩评论

暂无评论