超详细的嵌入式cJSON使用注意事项,持续补充中......

news2025/4/21 10:49:13

文章目录

  • 一、堆内存不足
    • 1.1 问题描述
    • 1.2 解决办法
  • 二、内存泄露
    • 2.1 忘记Delete
    • 2.2 忘记Free
    • 2.3 串口数据接收缺少部分字符导致的内存泄露(自己的问题)
      • 问题分析
    • 2.4 内存泄露在Cortex-M3内核会发生什么?

cJSON开源库地址: cJSON

一、堆内存不足

1.1 问题描述

这是大家遇到最多的问题之一,在Keil5中给STM32F103ZET6默认分配的堆大小是0x200,在数据量比较大的时候容易出现内存溢出错误。如果数据量不大,那么无所谓,但是数据量大了就到出现堆内存爆满。

1.2 解决办法

修改堆大小,找到.s文件,再找到堆大小,将0x200修改为0xf00,这个数根据需求定。

在这里插入图片描述

二、内存泄露

2.1 忘记Delete

在使用cJSON_Parse()函数解析json数据后,我们需要释放掉这个函数所申请的内存,因为设计到json嵌套的问题,所以需要使用cJSON库中的释放函数cJSON_Delete()函数

使用示例

root=cJSON_Parse(data);
if(root != NULL)
{
	/*
	对root进行进一步解析
	*/
	cJSON_Delete(root);	
}


2.2 忘记Free

在这里插入图片描述

在github主页可以看到这段话:使用cJSON_Print()这个函数打印json数据会申请一块内存,在使用完这个函数后你有义务释放掉这个函数所申请的内存

使用示例


char *json_string = cJSON_Print(item);
if (json_string) 
{
    printf("%s\n", json_string);
    free(json_string);
    //1.5版本以上也可以使用以下函数进行释放
    //cJSON_free(json_string);
    
}

2.3 串口数据接收缺少部分字符导致的内存泄露(自己的问题)

由于return造成的!惨痛的教训

*有问题的代码

//==================================================================
//函 数 名:pid_parameter_change
//功    能:接收字符串格式化为JSON格式,解析其中pid数据,并修改对应pid结构体
//输入参数:字符串指针,pid结构体指针
//返 回 值:0表示解析成功,1表示解析错误
//==================================================================
uint8_t pid_parameter_change(const char *data,PID *pid)
{
    cJSON *root=NULL;
    cJSON *kp=NULL,*ki=NULL,*kd=NULL;
    float p,i,d;
    
    root=cJSON_Parse(data);
    if(root!=NULL)
    {
        /* 解析Kp */
        kp=cJSON_GetObjectItem(root,"kp");
        if(kp == NULL)  return 1;
        else            p=cJSON_GetNumberValue(kp);
        /* 解析Ki */
        ki=cJSON_GetObjectItem(root,"ki");
        if(ki == NULL)  return 1;
        else            i=cJSON_GetNumberValue(ki);
        /* 解析Kd */
        kd=cJSON_GetObjectItem(root,"kd");
        if(kd == NULL)  return 1;
        else            d=cJSON_GetNumberValue(kd);
        /* 修改PID参数 */
        pid->Kp=p;
        pid->Ki=i;
        pid->Kd=d;
        
        printf("%p\r\n",root);
        cJSON_Delete(root);
        root=NULL,kp=NULL,ki=NULL,kd=NULL;
        return 0;
    }
    else{
        cJSON_Delete(root);
        root=NULL;
        return 1;   //root数据JSON格式化失败
    }
}

*修改后的代码

//==================================================================
//函 数 名:pid_parameter_change
//功    能:接收字符串格式化为JSON格式,解析其中pid数据,并修改对应pid结构体
//输入参数:字符串指针,pid结构体指针
//返 回 值:0表示解析成功,1表示解析错误
//==================================================================
uint8_t pid_parameter_change(const char *data,PID *pid)
{
    cJSON *root=NULL;
    cJSON *kp=NULL,*ki=NULL,*kd=NULL;
    float p,i,d;
    if(data[0] == '{' && data[UART1_Rx_cnt-3] == '}' )
    {
        root=cJSON_Parse(data);
        if(root)
        {
            /* 解析Kp */
            kp=cJSON_GetObjectItem(root,"kp");
            if(kp == NULL)  
            {    
                cJSON_Delete(root);
                return 1;
            }
            else            
            	p=cJSON_GetNumberValue(kp);
            
            /* 解析Ki */
            ki=cJSON_GetObjectItem(root,"ki");
            if(ki == NULL)  
            {    
                cJSON_Delete(root);
                return 1;
            }
            else            
            	i=cJSON_GetNumberValue(ki);
            /* 解析Kd */
            kd=cJSON_GetObjectItem(root,"kd");
            if(kd == NULL)  
            {    
                cJSON_Delete(root);
                return 1;
            }
            else            
            	d=cJSON_GetNumberValue(kd);
            /* 修改PID参数 */
            pid->Kp=p;
            pid->Ki=i;
            pid->Kd=d;
            /*删除JSON,否则可能会发送内存泄露*/
            cJSON_Delete(root);
            root=NULL,kp=NULL,ki=NULL,kd=NULL;  // 释放掉的内存指向NULL,避免产生野指针
            return 0;
        }
        else
        {
       		/*删除JSON,否则可能会发送内存泄露*/
            cJSON_Delete(root);
            root=NULL;
            return 1;   //root数据JSON格式化失败
        } 
    }
    else
    {
        return 1;
    }
}

问题分析

这个问题的根源是由于串口接收到的数据不全导致的!同时也有我代码不严谨的问题。
在有问题的那个代码中,使用cJSON_Parse()成功解析出数据,使得代码进入了if语句中 , if(root)
进入if语句后,再次判断json对象是否有ki这个元素
这个时候出现了问题,由于串口接收缺少了ki中的i这个字符,使得if语句判断结果未false,直接return了。 if(ki == NULL) .
注意,return之前我没有使用cJSON_Delete()删除root,所以出现了内存泄露!
写代码的时候没考虑到cJSON_Parse()成功后,解析不出json对象中的元素的问题!

2.4 内存泄露在Cortex-M3内核会发生什么?

未完待续…

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1383154.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ssh 远程登录协议

一、SSH 服务 1.1 SSH 基础 SSH(Secure Shell)是一种安全通道协议,主要用来实现字符界面的远程登录、远程 复制等功能。SSH 协议对通信双方的数据传输进行了加密处理,其中包括用户登录时输入的用户口令,SSH 为建立在应…

mongoose6.0版以上操作mongodb数据库的基本使用

1、介绍 Mongoose 是一个对象文档模型库,官网 http://www.mongoosejs.net/ 2、作用 方便使用代码操作 mongodb 数据库 3、使用流程 3.1、链接数据库 //1. 安装 mongoose---> npm install mongoose --save//2. 导入 mongoose const mongoose require(&quo…

PiflowX-DorisWrite组件

DorisWrite组件 组件说明 往Doris存储写入数据。 计算引擎 flink 组件分组 doris 端口 Inport:默认端口 outport:默认端口 组件属性 名称展示名称默认值允许值是否必填描述例子fenodesFenodes“”无是Doris FE http地址, 支持多个…

PLC-IoT 网关开发札记(4):Xamarin Forms 实现自定义控件(一个开关)

1. 需求 物联网项目中要集成大量的设备,作为一种简单的数字孪生手段,每一型号的设备都需要一个对应的虚拟实现,也就是用界面把这个设备呈现出来。设备有多个可管理的“属性”,对这个设备的监测对应获取这个设备“属性”的值&…

Linux系统编程(十一):高级 IO

参考引用 UNIX 环境高级编程 (第3版)嵌入式Linux C应用编程-正点原子 Linux系统编程(文章链接汇总) 1. 非阻塞 I/O 阻塞就是进入了休眠状态,交出了 CPU 控制权阻塞 I/O 就是对文件的 I/O 操作(读写操作)是阻塞式的&a…

FGSM方法生成交通信号牌的对抗图像样本

背景: 生成对抗样本,即扰动图像,让原本是“停车”的信号牌识别为“禁止驶入” 实验准备 模型:找一个训练好的,识别交通信号牌的CNN模型,灰度图像 模型地址:GitHub - Daulettulegenov/TSR_CNN:…

高级RAG(六): 句子-窗口检索

之前我们介绍了LlamaIndex的从小到大的检索 的检索方法,今天我们再来介绍llamaindex的另外一种高级检索方法: 句子-窗口检索(Sentence Window Retrieval),在开始介绍之前让我们先回顾一下基本的RAG检索的流程,如下图所示: 在执行基…

学会编写自定义configure脚本,轻松实现定制化配置

学会编写自定义configure脚本,轻松实现定制化配置 一、configure脚本的作用和重要性二、configure脚本的基本结构和语法三、编写自定义configure脚本的步骤四、示例五、常见的问题总结 一、configure脚本的作用和重要性 configure脚本是用于自动配置软件源代码的脚…

jmeter如何做接口测试?

Jmeter介绍&测试准备: Jmeter介绍:Jmeter是软件行业里面比较常用的接口、性能测试工具,下面介绍下如何用Jmeter做接口测试以及如何用它连接MySQL数据库。 前期准备:测试前,需要安装好Jmeter以及jdk并配置好jdk环…

高级JavaScript。同步和异步,阻塞和非阻塞

同步阻塞 同步非阻塞 异步阻塞 异步非阻塞 在当什么是同步和异步,阻塞与非阻塞的概念还没弄清楚之前,更别提上面这些组合术语了,只会让你更加困惑。 同步和异步 同步和异步其实指的是,请求发起方对消息结果的获取是主动发起…

强化学习应用(五):基于Q-learning算法的无人车配送路径规划(通过Python代码)

一、Q-learning算法介绍 Q-learning是一种强化学习算法,用于解决基于环境的决策问题。它通过学习一个Q-table来指导智能体在不同状态下采取最优动作。下面是Q-learning算法的基本步骤: 1. 定义环境:确定问题的状态和动作空间,并…

NI PXIe-6386国产替代,8路AI(16位,14 MS/s/ch),2路A​O,24路DIO,PXI多功能I/O模块

PXIe-6386 PXIe,8路AI(16位,14 MS/s/ch),2路A​O,24路DIO,PXI多功能I/O模块 PXIe-6386是一款同步采样的多功能DAQ设备。该模块提供了模拟 I/O、数字I/O、四个32位计数器和模拟和数字触发。板载N…

2024年【G1工业锅炉司炉】考试及G1工业锅炉司炉考试资料

题库来源:安全生产模拟考试一点通公众号小程序 G1工业锅炉司炉考试根据新G1工业锅炉司炉考试大纲要求,安全生产模拟考试一点通将G1工业锅炉司炉模拟考试试题进行汇编,组成一套G1工业锅炉司炉全真模拟考试试题,学员可通过G1工业锅…

【现代密码学】笔记3.1-3.3 --规约证明、伪随机性《introduction to modern cryphtography》

【现代密码学】笔记3.1-3.3 --规约证明、伪随机性《introduction to modern cryphtography》 写在最前面私钥加密与伪随机性 第一部分密码学的计算方法论计算安全加密的定义:对称加密算法 伪随机性伪随机生成器(PRG) 规约法规约证明 构造安全…

LeetCode刷题.15(哈希表与计数排序解决41. 缺失的第一个正数)

给你一个未排序的整数数组 nums ,请你找出其中没有出现的最小的正整数。 请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案。 示例 1: 输入:nums [1,2,0] 输出:3 示例 2: 输入:nums …

MCS-51---串行通信的特点

目录 一.同步通信和异步通信 1.异步通信 2.同步通信 二.串行通信的方式 1.单工 2.半双工 3.全双工 三.串行通信的速率 四.MCS-51单片机结构 五.串行口的控制 1.串行口控制寄存器(SCON) 2.电源控制寄存器(PCON) 六.波特率的设计 七.串行口的工作方式 1.方式0 2.…

NLP论文阅读记录 - WOS | ROUGE-SEM:使用ROUGE结合语义更好地评估摘要

文章目录 前言0、论文摘要一、Introduction1.1目标问题1.2相关的尝试1.3本文贡献 二.相关工作三.本文方法四 实验效果4.1数据集4.2 对比模型4.3实施细节4.4评估指标4.5 实验结果4.6 细粒度分析 五 总结 前言 ROUGE-SEM: Better evaluation of summarization using ROUGE combin…

操作系统详解(5.1)——信号(Signal)的相关题目

系列文章: 操作系统详解(1)——操作系统的作用 操作系统详解(2)——异常处理(Exception) 操作系统详解(3)——进程、并发和并行 操作系统详解(4)——进程控制(fork, waitpid, sleep, execve) 操作系统详解(5)——信号(Signal) 文章目录 题目第一问第二问第三问 题目…

python24.1.14while循环

当条件结束时间未知时,while循环比for循环更合适 实践

Debian(Linux)局域网共享文件-NFS

NFS (Network File system) 是一种客户端-服务器文件系统协议,允许多个系统或用户访问相同的共享文件夹或文件。最新版本是 NFS-V4,共享文件就像存储在本地一样。它提供了中央管理,可以使用防火墙和 Kerberos 身份验证进行保护。 本文将指导…