Arduino低内存环境下不使用String类的char字符串拼接方法咨询
原代码问题说明
原代码存在两个不可运行的底层问题:
char *aa = "2"指向的是只读的字符串常量区,strcat向该地址写入会直接触发内存访问错误aa指向的内存空间仅能容纳2个字符(字符'2' + 结束符'\0'),没有多余空间存储拼接后的内容
以下是适配2kB内存Arduino设备、无需使用String类的可行方案,全部采用栈/静态内存,无堆碎片风险:
方案1:静态缓冲拼接函数(最适合单线程Arduino场景,可直接当参数传入)
Arduino为单线程运行,不存在重入冲突,用固定大小的静态缓冲做拼接,内存占用固定,用完不需要手动释放:
// 缓冲大小根据实际需要拼接的最大字符串总长度调整,不要超过内存上限 #define CONCAT_BUF_SIZE 32 char* str_concat(const char* str1, const char* str2) { static char buf[CONCAT_BUF_SIZE]; // 清空缓冲避免旧数据干扰 memset(buf, 0, CONCAT_BUF_SIZE); int len1 = strlen(str1); int len2 = strlen(str2); // 防止缓冲溢出,超过长度自动截断 if (len1 + len2 >= CONCAT_BUF_SIZE) { len2 = CONCAT_BUF_SIZE - len1 - 1; } memcpy(buf, str1, len1); memcpy(buf + len1, str2, len2); buf[len1 + len2] = '\0'; return buf; } // 你的原有业务函数 void abca(char *a) { Serial.println(a); } void setup() { Serial.begin(9600); char *bb = "1"; char *aa = "2"; // 直接传入拼接结果即可 abca(str_concat(aa, bb)); }
注意:该方案的静态缓冲每次调用会被覆盖,如果你需要同时留存多份拼接结果,不要使用该方案。
方案2:调用方预分配缓冲(无重入问题,最安全)
如果有多个拼接结果需要同时留存,或怕静态缓冲被覆盖,由调用方提前在栈上分配足够空间传入即可,内存自动回收:
char* str_concat(char* dst_buf, const char* str1, const char* str2, int buf_size) { memset(dst_buf, 0, buf_size); int len1 = strlen(str1); int len2 = strlen(str2); if (len1 + len2 >= buf_size) { len2 = buf_size - len1 - 1; } memcpy(dst_buf, str1, len1); memcpy(dst_buf + len1, str2, len2); dst_buf[len1 + len2] = '\0'; return dst_buf; } void abca(char *a) { Serial.println(a); } void setup() { Serial.begin(9600); char *bb = "1"; char *aa = "2"; // 栈上分配刚好够用的缓冲,总长度+1(结束符)即可 char concat_buf[32]; abca(str_concat(concat_buf, aa, bb, sizeof(concat_buf))); }
方案3:C99复合字面量简化写法(不需要单独定义拼接函数)
Arduino的GCC编译器支持C99标准,可以直接用复合字面量在调用时一次性分配栈缓冲,写法更简洁:
void abca(char *a) { Serial.println(a); } void setup() { Serial.begin(9600); char *bb = "1"; char *aa = "2"; // 32为缓冲大小,根据实际需求调整即可 abca(strcat(strcpy((char[32]){}, aa), bb)); }
通用注意事项
- 所有方案中的缓冲大小请根据实际拼接的字符串最大总长度调整,不要设置过大浪费内存,也不要过小导致字符串被截断
- 不要在2kB级小内存设备上使用
malloc/free动态分配拼接内存,极易产生堆碎片,最终导致内存不足崩溃
内容的提问来源于stack exchange,提问作者Michal
相关产品推荐
相关产品推荐

