Делаю на ESP32 парсинг JSON-строки, которая приходит от сервера, чтобы извлечь оттуда URL. Столкнулся с ошибкой освобождения памяти при вызове cJSON_Delete.
do
{
jroot = cJSON_Parse(src);
if (NULL == jroot)
break;
// здесь все норм
jurl = cJSON_GetObjectItem(jroot, "fw_url");
if (NULL == jurl)
break;
// здесь все норм
if(!cJSON_IsString(jurl) || NULL == jurl->valuestring)
break;
// здесь все норм, делаю копию jurl->valuestring
strcpy(dst, jurl->valuestring);
result = true;
} while (false);
if (jurl)
cJSON_Delete(jurl); // здесь все норм
if (jroot)
cJSON_Delete(jroot); // а вот тут падение программы
В логе сообщение об ошибке:
assert failed: 0x4038ef6a
0x4038ef6a: tlsf_free at ~/esp-idf/components/heap/heap_tlsf.c:872 (discriminator 1)
Посмотрел в heap_tlsf.c строку 872, срабатывает вот этот assert:
tlsf_assert(!block_is_free(block) && "block already marked as free");
Причина оказалась в особенности реализации библиотеки cJSON. Необходимо удалять только родительский объект (в этом примере jroot), потому что удаление родительского объекта автоматически удалит дочерние объекты (в этом примере jurl). Вызов cJSON_Delete(jroot) пытался удалить дочерний объект jurl, который уже был удален, поэтому возникала ошибка.
Исправление ошибки:
//if (jurl)
// cJSON_Delete(jurl);
if (jroot)
cJSON_Delete(jroot);
Основное правило удаления объектов cJSON: не надо удалять дочерние объекты.
[Ссылки]
1. DaveGamble / cJSON site:github.com. 2. xuzd / cJSON site:gitee.com. |