cJSON源码解析之add_item_to_object函数

news2024/11/20 3:27:04

文章目录

  • 前言
  • add_item_to_object函数是干什么的
  • add_item_to_object代码解析
    • 函数实现
    • 函数原理解析
      • 开头的代码
      • constant_key参数的作用
      • 最后的if判断
    • add_item_to_array函数
  • 总结


前言

在我们的日常编程中,JSON已经成为了一种非常常见的数据交换格式。在C语言中,我们通常使用cJSON库来处理JSON数据。cJSON库提供了一系列的函数来创建、解析和操作JSON数据。其中,add_item_to_object函数是一个关键的函数,它允许我们将一个新的项目添加到一个已存在的JSON对象中。在这篇文章中,我们将深入探讨add_item_to_object函数的内部实现。


add_item_to_object函数是干什么的

这个函数用于把一个item添加到一个对象里面
他是cJSON_AddItemToObjectcJSON_AddItemToObjectCS函数的实现
在这里插入图片描述

add_item_to_object代码解析

函数实现

static cJSON_bool add_item_to_object(cJSON * const object, const char * const string, cJSON * const item, const internal_hooks * const hooks, const cJSON_bool constant_key)
{
    char *new_key = NULL;
    int new_type = cJSON_Invalid;

    if ((object == NULL) || (string == NULL) || (item == NULL) || (object == item))
    {
        return false;
    }

    if (constant_key)
    {
        new_key = (char*)cast_away_const(string);
        new_type = item->type | cJSON_StringIsConst;
    }
    else
    {
        new_key = (char*)cJSON_strdup((const unsigned char*)string, hooks);
        if (new_key == NULL)
        {
            return false;
        }

        new_type = item->type & ~cJSON_StringIsConst;
    }

    if (!(item->type & cJSON_StringIsConst) && (item->string != NULL))
    {
        hooks->deallocate(item->string);
    }

    item->string = new_key;
    item->type = new_type;

    return add_item_to_array(object, item);
}

函数原理解析

开头的代码

首先他声明了两个变量,用于存储key值和该键值对的类型的

char *new_key = NULL;
int new_type = cJSON_Invalid;

紧接着,他去判断参数的合法性,为了内存的异常安全:‘

if ((object == NULL) || (string == NULL) || (item == NULL) || (object == item))
{
    return false;
}

constant_key参数的作用

在cJSON库中,constant_key参数是一个布尔值,它决定了键(key)是进行深拷贝还是浅拷贝。

如果constant_keytrue,那么在添加新项目到JSON对象时,键(key)将会进行浅拷贝,也就是直接复制指针。这意味着,如果原始的键字符串在以后被修改或释放,那么存储在JSON对象中的键也会受到影响。

如果constant_keyfalse,那么键(key)将会进行深拷贝,也就是复制整个字符串的内容到新的内存位置。这样,即使原始的键字符串在以后被修改或释放,也不会影响到存储在JSON对象中的键。

可以看到他的浅拷贝和深拷贝是完全不同的:
在这里插入图片描述

if (constant_key)
{
    new_key = (char*)cast_away_const(string);
    new_type = item->type | cJSON_StringIsConst;
}
else
{
    new_key = (char*)cJSON_strdup((const unsigned char*)string, hooks);
    if (new_key == NULL)
    {
        return false;
    }

    new_type = item->type & ~cJSON_StringIsConst;
}

在浅拷贝,直接把参数的key复制到new_key里面,然后把new_type赋值成对应的类型
那么他为何要| cJSON_StringIsConst
这行代码中的|操作符是C语言中的位运算符,表示按位或操作。cJSON_StringIsConst是一个常量,它的值通常为0x200,在二进制中表示为10 0000 0000

当我们执行item->type | cJSON_StringIsConst时,实际上是将item->type的值和cJSON_StringIsConst的值进行按位或操作。这样做的目的是为了将item->type的第10位设置为1,而不改变其他位的值。

这里cJSON_StringIsConst的作用是标记字符串是否为常量。如果一个字符串被标记为常量,那么在cJSON对象被删除时,这个字符串就不会被释放。这对于那些指向静态或全局变量的字符串非常有用,因为这些字符串不能被释放。

所以,new_type = item->type | cJSON_StringIsConst;这行代码的作用就是将item->type的值更新为新的类型,同时保留了原来的类型信息,并标记字符串为常量。

在深拷贝中,使用了cJSON_strdup函数复制字符串,我们上篇文章已经介绍过
new_type = item->type & ~cJSON_StringIsConst;类比上面,这段代码也就是把这个字符串标记为不是常量,在删除的时候可以直接删除

最后的if判断

这段代码的目的是在添加新的键值对到JSON对象时,安全地处理旧的键字符串。

if (!(item->type & cJSON_StringIsConst) && (item->string != NULL))
{
    hooks->deallocate(item->string);
}

!(item->type & cJSON_StringIsConst)这段的含义如下:
这部分是检查item->type是否被标记为cJSON_StringIsConst。如果被标记为cJSON_StringIsConst,那么这个字符串就是一个常量字符串,我们不能释放它。所以,我们使用!操作符来检查item->type是否没有被标记为cJSON_StringIsConst

(item->string != NULL):这部分是检查item->string是否为NULL。如果item->string为NULL,那么我们就没有必要(也不能)释放它。

最后就把参数item复制上我们的状态就行了:

item->string = new_key;
item->type = new_type;

我们会发现,这个函数仅仅是把键和type安装到参数type上面,但是还没有进行item安装到object上面,所以这里又出现一个函数,他专门用于链表的操作的add_item_to_array

add_item_to_array函数

add_item_to_array函数代码如下:

static cJSON_bool add_item_to_array(cJSON *array, cJSON *item)
{
    cJSON *child = NULL;

    if ((item == NULL) || (array == NULL) || (array == item))
    {
        return false;
    }

    child = array->child;
    /*
     * To find the last item in array quickly, we use prev in array
     */
    if (child == NULL)
    {
        /* list is empty, start new one */
        array->child = item;
        item->prev = item;
        item->next = NULL;
    }
    else
    {
        /* append to the end */
        if (child->prev)
        {
            suffix_object(child->prev, item);
            array->child->prev = item;
        }
    }

    return true;
}

他的主要代码如下:

if (child == NULL)
{
    /* list is empty, start new one */
    array->child = item;
    item->prev = item;
    item->next = NULL;
}
else
{
    /* append to the end */
    if (child->prev)
    {
        suffix_object(child->prev, item);
        array->child->prev = item;
    }
}

他的图例:

  1. 如果链表为空 (child == NULL),那么新的item就会成为链表的第一个(也是唯一的)元素。这个情况可以用下面的字符画来表示:
array
  |
  v
 item  --> NULL
  ^
  |
 item

在这个图中,array->child指向itemitem->prev指向item自身,item->next指向NULL

  1. 如果链表不为空,那么新的item就会被添加到链表的末尾。这个情况可以用下面的字符画来表示:
array
  |
  v
 child1 <--> child2 <--> ... <--> childN <--> item --> NULL
  ^                                          ^
  |                                          |
 child1                                    item

在这个图中,array->child指向链表的第一个元素child1childN->next指向新的itemitem->prev指向childNitem->next指向NULL

这样就成功的把我们的item连接到object里面了


总结

通过深入研究add_item_to_object函数的源码,我们可以更好地理解cJSON库是如何处理JSON对象的。这个函数的实现虽然简单,但却非常关键,它使得我们可以方便地向JSON对象中添加新的项目。希望这篇文章能帮助你更好地理解cJSON库的内部工作原理,以及如何在你自己的项目中使用它。

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

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

相关文章

【Android】android studio简单实现图书馆借阅管理系统

希望文章能给到你启发和灵感&#xff5e; 点赞收藏关注 支持一下吧&#xff5e; 阅读指南 序幕一、基础环境说明1.1 硬件环境1.2 软件环境 二、整体设计2.1 数据库逻辑处理&#xff1a;2.2 登录/注册模块2.3 功能界面初始化&#xff1a;2.4 图书管理模块2.5 图书租借服务2.6 读…

[物联网专题] - 螺钉式接线端子的选择和辨识

工业设备上大量使用各式各样的端子来连接外部设备和电缆电线&#xff0c;其中用得最多的就是标准的螺钉式端子&#xff0c;其外形如下&#xff1a; 标准端子一般是2位&#xff08;2个接线端子&#xff09;&#xff0c;端子与端子之间可以级联&#xff0c;组成任意数量的位数。…

vue项目无后台版本打包上传到服务器

打包项目 也可以在文件目录下npm run build 生成dist文件夹 将dist文件夹里的所有文件拷贝到站点的根目录&#xff0c;这里使用宝塔面板进行操作 前提你得先创建站点&#xff0c;域名绑定等操作

项目菜单配置

stores/index.js import {createStore } from "vuex"; //计算高度 let height window.innerHeight;//计算分辨率 let width window.innerWidth;let activeIndex localStorage.getItem("activeIndex"); if (activeIndex null || activeIndex "&q…

制图工具(14)导出图层字段属性信息表

在制图工具&#xff08;13&#xff09;地理数据库初始化工具中我们提到&#xff0c;有一个参数为&#xff1a;“输入Excel表”&#xff0c;并要求表格中的图层字段属性项需要按工具的帮助文档中的示例进行组织… 如下图&#xff1a; 此外&#xff0c;总有那个一个特别的需求&am…

【单片机毕业设计选题24028】-基于STM32的大棚温湿度采集系统

系统功能: 系统分为手动和自动模式&#xff0c;上电默认为自动模式&#xff0c;自动模式下系统根据采集到的传感器值 自动控制&#xff0c;温度过低后自动开启加热&#xff0c;湿度过高后自动开启通风&#xff0c;光照过低后自动开启补 光&#xff0c;水位过低后自动开启水泵…

Android 界面库 (二) 之 Data binding 详细介绍

1. 简介 回顾我们在前面文章《Android 界面库 (一) 之 View binding 简单使用》中学习的 View Binding&#xff0c;它旨在简化 View 与代码之间的绑定过程。它会在编译时期为每个 XML 布局文件生成相应的绑定类(Binding class)&#xff0c;该类里包含了布局文件每个有 ID 的 Vi…

L59---101.对称二叉树(广搜)---Java版

1.题目描述 2.思路和知识点 &#xff08;1)根节点为空&#xff1a; 如果根节点为空&#xff0c;树是对称的。 (2)递归检查&#xff1a; isMirror 方法递归检查两个子树是否是镜像对称的。 (3)辅助函数 isMirror&#xff1a; 1)如果两个节点都为空&#xff0c;它们是镜像对称的…

php composer 报错

引用文章&#xff1a; Composer设置国内镜像_composer 国内源-CSDN博客 php composer.phar require --prefer-dist yiidoc/yii2-redactor "*" A connection timeout was encountered. If you intend to run Composer without connecting to the internet, run the …

day49---数据结构与算法(四)

三. 基础算法 3.1 查找概述 查找算法是一种在数据集中寻找特定数据项的方法。通常&#xff0c;数据集是在计算机程序中存储的&#xff0c;例如数组、链表或散列表。在编写程序时&#xff0c;查找算法是非常重要的&#xff0c;它有助于快速找到所需的数据。在本文中&#xff0…

Linux系统安装Lua语言及Lua外部库

安装Lua Lua语言是一种轻量级、高效且可扩展的脚本语言&#xff0c;具有简洁易学的语法和占用资源少的特点。它支持动态类型&#xff0c;提供了丰富的表达式和运算符&#xff0c;同时具备自动垃圾回收机制和跨平台性。Lua语言易于嵌入到其他应用程序中&#xff0c;并可与其他语…

高性能并行计算华为云实验五:PageRank算法实验

目录 一、实验目的 二、实验说明 三、实验过程 3.1 创建PageRank源码 3.2 makefile的创建和编译 3.3 主机配置文件建立与运行监测 四、实验结果与分析 4.1 采用默认的节点数量及迭代次数进行测试 4.2 分析并行化下节点数量与耗时的变化规律 4.3 分析迭代次数与耗时的变…

2024广东省职业技能大赛云计算赛项实战——集群部署GitLab Agent

集群部署GitLab Agent 前言 题目如下&#xff1a; 部署GitLab Agent 将Kubernetes集群添加到demo-2048项目中&#xff0c;并命名为kubernetes-agent&#xff0c;项目命名空间选择gitlab-ci。 说是部署GitLab Agent,但据我了解&#xff0c;Agent就是Runner&#xff0c;看题目…

5.How Fast Should You Be When Learning?(你应该用多快的速度学习? (一))

Normally when I talk about learing quickly, I’m using speed as a synonym for efficiency.Use more effective methods and you’ll learn more in less time.All else being equal, that means you’re learing faster. 通常我在谈到快速学习时&#xff0c;是把“速度&qu…

【神经网络】神经元的基本结构和训练过程

&#x1f388;个人主页&#xff1a;豌豆射手^ &#x1f389;欢迎 &#x1f44d;点赞✍评论⭐收藏 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共同学习、交流进步&#xff01; 神经元的基本结构和训练过程 …

Redis数据库(三):Redis数据库三种特殊数据类型

除了上一篇博客讲的五种基本数据类型外&#xff0c;Redis还有三种特殊的数据类型&#xff0c;它们有着不同的应用场景&#xff0c;这一篇博客&#xff0c;我们来学习它。 目录 一、geospatial 地理空间 1.1 添加地理位置 1.2 返回给定名称的纬度和经度 1.3 返回两个给定位…

小柴冲刺嵌入式系统设计师系列总目录

工作两年 逐渐意识到基础知识的重要性✌️ 意识到掌握了这个证书好像就已经掌握了80%工作中用到的知识了。剩下的就在工作的实战中学习 来和小柴一起冲刺软考吧&#xff01;加油&#x1f61c; 【小柴冲刺软考中级嵌入式系统设计师系列】总目录 前言 专栏目标&#xff1a;冲刺…

ros2_control 使用教程

系列文章目录 前言 0.1 欢迎阅读 ros2_control 文档&#xff01; ros2_control 是一个使用&#xff08;ROS 2&#xff09;对机器人进行&#xff08;实时&#xff09;控制的框架。其软件包是对 ROS&#xff08;机器人操作系统&#xff09;中使用的 ros_control 软件包的重写。r…

NetSuite CSV导入类型与记录类型梳理

最近有用户问到我们的一个问题是&#xff0c;哪些数据可以使用CSV导入&#xff0c;哪些数据不能使用CSV导入&#xff0c;干脆咱们就整理出来可使用CSV导入功能的类型和记录类型&#xff0c;供大家直接参考&#xff5e; 但是有一些内容或多或少由于每个企业的环境不一样而有所不…

jenkins环境搭建--关于jenkins在Ubuntu下的安装篇(一)

在ubuntu下使用命令进行下载安装包&#xff1a; 关于jenkins的安装有多种&#xff0c;可以借助docker容器进行安装&#xff0c;也可以通过传统方法手动一步步的进行安装&#xff0c;以下介绍手动一步步的安装方法&#xff0c;后续我们将解释关于jenkins的相关配置以及实战使用…