Skip to content

3.4 使用动态内存 ​

本节导读

写程序时,我们常常事先不知道要存多少数据——今天存 3 个,明天可能存 100 个。怎么办?

答案:动态内存——在程序运行的过程中,按实际需要随时"借"、用完再"还"。这一节我们看 Z-Stack 怎么借、怎么还,以及借了不还的下场。

一、什么是动态内存? ​

在编写程序时,开发者可能不能确定需要使用多少内存来保存数据,希望在程序运行的过程中根据实际需要来使用相应的内存空间。这种能够在程序运行过程中申请和释放的内存空间称为动态内存。

生活类比:静态内存像买房子——面积买死了,住不满浪费、不够住尴尬;动态内存像住酒店——按晚开房(申请)、退房(释放),住几晚开几晚,灵活又省心。

Z-Stack 3.0 中动态内存分配的 API 在 OSAL_Memory.h 文件中(OSAL 目录下):

OSAL_Memory.h 在工程中的位置

二、申请与释放:一对"黄金搭档" ​

在 OSAL_Memory.h 中可以找到两个最重要的 API:

c
/**
 * @brief 动态申请内存空间
 * @param size - 申请多少个字节的内存空间
 * @return 返回该内存空间的指针
 */
void *osal_mem_alloc( uint16 size );

/**
 * @brief 动态释放内存空间
 * @param ptr - 待释放的内存空间指针
 */
void osal_mem_free( void *ptr );

使用规则只有一条,但必须刻在脑子里:

⚠️ 铁律:申请的内存空间不用了,一定要调用 osal_mem_free() 释放!

借了不还(不释放)会怎样? 内存空间会越来越少,程序跑着跑着就会内存不足、甚至崩溃——这就是传说中的内存泄漏。酒店房间被你占着不退,后来的客人就没房住了。

三、内存操作:借到之后怎么用 ​

申请完动态内存,可以用内存操作 API 来使用它。最常用的两个定义在 OSAL.h 中:

c
/**
 * @brief 把源内存空间的内容复制到目标内存空间
 * @param dest - 目标内存空间
 * @param src  - 源内存空间
 * @param len  - 复制多少个字节
 */
void *osal_memcpy(void *dest, const void GENERIC *src, unsigned int len);

/**
 * @brief 把从 dest 起的 len 个字节的值都设置为 value
 * @param dest  - 内存空间
 * @param value - 指定的值
 * @param len   - 要设置的字节数
 */
void *osal_memset(void *dest, uint8 value, int len);
API作用类比
osal_memcpy把一块内存的内容搬到另一块把 A 文件的内容复印到 B 文件
osal_memset把一块内存全部设成同一个值把房间全部刷成同一种颜色(常用 0 清零)

四、实战:借内存打印 "Hello World!"(每 3 秒一次) ​

把 3.3 节的定时器事件和动态内存组合起来,完整流程走一遍。

在事件处理函数中:

c
if (events & SAMPLEAPP_TEST_EVT) {
    // 源字符串
    char *str = "Hello World!\n";

    // ① 从堆空间中申请 32 个字节的内存
    char *mem = osal_mem_alloc(32);

    // ② 如果申请成功
    if (mem != NULL) {
        osal_memset(mem, 0, 32);                 // ③ 先把内存清零(干净的房间)
        osal_memcpy(mem, str, osal_strlen(str)); // ④ 把字符串拷贝进去
        printf(mem);                             // ⑤ 打印内存里的内容
        osal_mem_free(mem);                      // ⑥ ★ 用完必须释放!★
    }

    // ⑦ 重新触发事件:3 秒后再执行一次
    osal_start_timerEx(zclSampleSw_TaskID, SAMPLEAPP_TEST_EVT, 3000);

    // ⑧ 清除已处理的事件,返回未处理的事件
    return (events ^ SAMPLEAPP_TEST_EVT);
}

这段代码干了什么?

步骤代码大白话
①osal_mem_alloc(32)开房:借 32 字节
③osal_memset(mem, 0, 32)保洁:先清零
④osal_memcpy(...)入住:把字符串放进去
⑤printf(mem)展示:打印出来
⑥osal_mem_free(mem)退房:释放!
⑦osal_start_timerEx(..., 3000)定闹钟:3 秒后再来一次

效果:程序每 3 秒打印一次 "Hello World!",并且每次都是"借 → 用 → 还"的完整循环——内存永远不会泄漏。

五、调试仿真:跑起来看效果 ​

第一步:下载并进入仿真 ​

使用仿真器把 ZigBee 开发板连接到电脑,单击 Download and Debug(Ctrl+D)按钮进行程序的编译、链接和下载,并进入仿真模式。

第二步:运行程序 ​

进入仿真模式之后,单击 Go(F5)按钮运行程序。

第三步:观察输出 ​

程序运行后,会每隔 3 秒在 Terminal I/O 窗口中输出一次 "Hello World!":

Terminal I/O 每 3 秒输出 Hello World

看到连续刷屏的 "Hello World!",说明:定时器事件在周期触发 → 动态内存申请/拷贝/释放正常 → 整个链路跑通了!

六、小结 ​

问题答案
什么是动态内存?程序运行时按需申请、用完释放的内存
申请 API?osal_mem_alloc(size)
释放 API?osal_mem_free(ptr) —— 必须调用!
内存操作 API?osal_memcpy(复制)/ osal_memset(清零/填充)
不释放的后果?内存泄漏:可用内存越来越少,程序最终崩溃
怎么让事件周期执行?处理完事件再 osal_start_timerEx 触发自己

一句话总结:动态内存 = "借(alloc)→ 用(cpy/set)→ 还(free)"三段式,配合定时器事件就能做出"每 3 秒报一次时"的周期任务。记住铁律:借了必须还,不还就泄漏。


内容来源:紫甲 IoT《ZigBee 3.0 开发指南》第三部分·3.4 节,经本站整理优化以便于理解;原站链接

aioteer · 物联网专业课程群

💬AI 伴学配置