You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ESP32 ESP-IDF中cJSON、缓冲区及数据结构的动态内存分配问题

解决ESP32 ESP-IDF开发中的内存分配与cJSON解析问题

一、动态内存分配读取文件

原读取函数存在文件打开失败未处理、读取长度错误、未动态分配内存等问题。正确实现需通过fseek获取文件大小,用malloc分配对应内存,读取后添加字符串终止符并关闭文件。

修正后的读取函数:

#define TAG "FILE"

char *read(const char *path)
{
    FILE *f = fopen(path, "r");
    if (f == NULL) {
        ESP_LOGE(TAG, "Could not open file for reading");
        return NULL;
    }

    // 获取文件大小
    fseek(f, 0, SEEK_END);
    long size = ftell(f);
    fseek(f, 0, SEEK_SET);

    // 动态分配内存,额外+1存储字符串终止符
    char *buffer = (char *)malloc(size + 1);
    if (buffer == NULL) {
        ESP_LOGE(TAG, "Failed to allocate memory for file buffer");
        fclose(f);
        return NULL;
    }

    // 读取文件内容
    size_t read_len = fread(buffer, 1, size, f);
    if (read_len != size) {
        ESP_LOGE(TAG, "Failed to read entire file, read %zu bytes out of %ld", read_len, size);
        free(buffer);
        fclose(f);
        return NULL;
    }

    // 添加字符串终止符,确保cJSON能正确解析
    buffer[size] = '\0';

    fclose(f);
    return buffer;
}

二、修复cJSON解析的内存断言错误

触发assert failed: tlsf_free tlsf.c:1120的核心原因:

  1. 循环内重复调用cJSON_Delete(json),第一次循环就释放了根节点,后续循环访问已释放内存导致重复free;
  2. cJSON_Parse(*buffer)传入参数错误,应直接传buffer字符串指针而非单个字符;
  3. json == NULL时调用cJSON_Delete(json)会触发未定义行为。

修正后的JSON解析函数:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "cJSON.h"
#include "esp_log.h"

#define TAG "JSON"

typedef struct caststruct{
    int value1;
    int value2;
    int value3;
    int value4;
    int value5;
} CastStruct;

// 动态分配结构体数组,避免固定大小限制
CastStruct *sdata = NULL;
int sdata_count = 0;

void jsonread(){
    char filename[] = "/test1";
    ESP_LOGI(TAG,"Step1: Reading file");
    
    char *buffer = read(filename);
    if (buffer == NULL) {
        ESP_LOGE(TAG, "Failed to load file");
        return;
    }
    ESP_LOGI(TAG,"Step2: File loaded, size: %zu", strlen(buffer));

    // 解析JSON,传入buffer指针而非*buffer
    cJSON *json = cJSON_Parse(buffer);
    if (json == NULL) {
        const char *error_ptr = cJSON_GetErrorPtr();
        if (error_ptr != NULL) {
            ESP_LOGE(TAG, "Parse error: %s", error_ptr);
        }
        free(buffer);
        return;
    }
    ESP_LOGI(TAG,"Step3: JSON parsed successfully");

    // 获取数组元素数量,动态分配结构体数组
    sdata_count = cJSON_GetArraySize(json);
    sdata = (CastStruct *)malloc(sizeof(CastStruct) * sdata_count);
    if (sdata == NULL) {
        ESP_LOGE(TAG, "Failed to allocate memory for sdata");
        cJSON_Delete(json);
        free(buffer);
        return;
    }

    int i = 0;
    cJSON *iter;
    cJSON_ArrayForEach(iter, json){
        ESP_LOGI(TAG,"Processing array element %d", i);
        cJSON *valuex = cJSON_GetObjectItemCaseSensitive(iter,"valuex");
        cJSON *valuei = cJSON_GetObjectItemCaseSensitive(iter,"valuei");
        
        // 确保字段存在且为数字
        if(cJSON_IsNumber(valuex) && cJSON_IsNumber(valuei)) {
            sdata[i].value1 = cJSON_GetNumberValue(valuex);
            sdata[i].value2 = 34;
            sdata[i].value3 = cJSON_GetNumberValue(valuei);
            i++;
        }
        // 注意:不要在这里调用cJSON_Delete(json)!
    }

    // 打印解析结果
    for(int j=0 ; j<sdata_count;j++) {
        ESP_LOGI(TAG,"value1[%d] = %d", j, sdata[j].value1);
        ESP_LOGI(TAG,"value2[%d] = %d", j, sdata[j].value2);
        ESP_LOGI(TAG,"value3[%d] = %d", j, sdata[j].value3);
    }

    // 释放所有动态分配的内存
    cJSON_Delete(json);
    free(buffer);
    free(sdata);
    sdata = NULL;
}

三、内存分配最佳实践与替代JSON解析器

内存分配要点

  1. Buffer:根据实际文件大小动态分配,避免全局大数组浪费内存;使用完成后必须调用free释放。
  2. cJSON对象:根节点仅需在解析完成后调用一次cJSON_Delete释放,循环内无需重复释放;遍历节点由根节点统一管理,无需单独释放。
  3. 结构体数组sdata:先获取JSON数组长度,再动态分配对应内存,避免固定大小限制;使用完成后调用free释放。

ESP32可用的替代JSON解析器

  1. jsmn:ESP-IDF自带轻量级解析器,基于token解析,内存占用极低,无需动态内存分配,适合资源有限场景。
  2. esp-tinyjson:专为ESP32优化的小型JSON解析/生成库,API简单,内存占用小。
  3. nlohmann/json:C++风格JSON库,API易用,但编译后体积较大,适合内存充足、追求代码可读性的场景。

内容的提问来源于stack exchange,提问作者harryw

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 17:35:55