嵌入式C中不使用malloc如何重定义运行时可变维度二维数组
问题说明
旧有嵌入式代码原本通过编译期宏切换屏幕方向,定义对应维度的二维数组some_array,重构时需要改为运行时判断方向,存在以下约束:
- 禁止使用
malloc等动态内存分配接口 - 不能修改现有代码中
some_array[i][j]的数组访问形式 - RAM资源紧张,不允许定义超出实际需求的冗余数组
- 横竖两种方向下数组总大小固定为200字节,仅行、列维度互换
之前尝试的两种方案均失效:
- 固定单种维度定义二维数组:运行时内存访问偏移计算错误,逻辑异常
- 定义
uint8_t**类型指针指向200字节连续内存:二级指针寻址逻辑和连续内存布局不匹配,程序运行异常
可行实现方案
采用静态数据缓冲区+行地址映射表的实现方式,完全满足所有约束,代码如下:
#include <stdint.h> // 实际数据存储区,总大小固定为10*20=200字节,无冗余占用 static uint8_t array_buf[10 * 20]; // 二维访问入口:行指针数组大小取两种方向下TX的最大值20,RAM开销极低 // 32位MCU下仅占20*4=80字节,8位MCU下仅占20*2=40字节 uint8_t *some_array[20]; uint8_t tx = 10; uint8_t ty = 20; extern uint8_t is_vertical; void init(){ if(is_vertical){ tx = 20; ty = 10; // 垂直方向:共20行,每行10字节,初始化每行首地址 for(uint8_t i = 0; i < tx; i++){ some_array[i] = &array_buf[i * ty]; } } else { tx = 10; ty = 20; // 水平方向:共10行,每行20字节,初始化每行首地址 for(uint8_t i = 0; i < tx; i++){ some_array[i] = &array_buf[i * ty]; } } }
方案说明
- 原有
some_array[i][j]的访问形式完全不需要修改,所有存量调用代码可直接复用 - 所有内存均为静态分配,无动态内存申请,符合嵌入式开发规范
- 无冗余RAM占用:数据区正好200字节,行指针表的额外开销远小于直接定义20*20冗余二维数组的方案
- 仅在初始化阶段执行一次行地址赋值,后续数组访问的效率和原生编译期定义的二维数组基本一致,无额外运行时开销
- 规避了之前两种方案的问题:
- 不需要依赖编译期固定的行长度计算偏移,访问时自动按初始化好的行地址寻址,不会出现偏移计算错误
- 行指针数组是真实存在的指针列表,访问
[i][j]时先取第i行的首地址再偏移j,和C语言二维数组的访问逻辑完全兼容,不会出现地址解析错误
注意:如果使用的编译器支持C99标准且栈空间充足,也可在函数内使用可变长度数组(VLA),但全局作用域无法使用VLA,且栈空间受限的嵌入式场景下不推荐该方式,上述静态分配方案兼容性最好。
内容的提问来源于stack exchange,提问作者Invader Zim
相关产品推荐
相关产品推荐

