cJSON源码解析之add_item_to_object函数

文章目录

  • 前言
  • 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|vitem  --> NULL^|item

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

  1. 如果链表不为空,那么新的item就会被添加到链表的末尾。这个情况可以用下面的字符画来表示:
array|vchild1 <--> 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.mzph.cn/news/863819.shtml

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

相关文章

MySQL 索引之外的相关查询优化总结

在这之前先说明几个概念&#xff1a; 1、驱动表和被驱动表&#xff1a;驱动表是主表&#xff0c;被驱动表是从表、非驱动表。驱动表和被驱动表并非根据 from 后面表名的先后顺序而确定&#xff0c;而是根据 explain 语句查询得到的顺序确定&#xff1b;展示在前面的是驱动表&am…

UI Toolkit系统学习

UI Toolkit 此文章用于学习UnityUI系统&#xff0c;手头的项目做完会来完善 官方文档 Unity上方菜单栏点击Window->UI Toolkit->Samples可以看UI Toolkit中的很多样例 使用 UI Toolkit 和 UI Builder 制作物品编辑器 在文件夹中右键->Create->UI Toolkit->Edi…

英特尔 Gaudi 加速辅助生成

随着模型规模的增长&#xff0c;生成式人工智能的实现需要大量的推理资源。这不仅增加了每次生成的成本&#xff0c;而且还增加了用于满足此类请求的功耗。因此&#xff0c;文本生成的推理优化对于降低延迟、基础设施成本以及功耗都至关重要&#xff0c;其可以改善用户体验并提…

leetCode-hot100-动态规划专题

动态规划 动态规划定义动态规划的核心思想动态规划的基本特征动态规划的基本思路例题322.零钱兑换53.最大子数组和72.编辑距离139.单词拆分62.不同路径63.不同路径Ⅱ64.最小路径和70.爬楼梯121.买卖股票的最佳时机152.乘积最大子数组 动态规划定义 动态规划&#xff08;Dynami…

【训练篇】MLU370-M8 完成 qwen1.5-7b-chat-lora训练及推理

文章目录 前言一、平台环境配置二、环境 or 模型准备1.模型下载2.环境准备2.1 modelscope2.2 transformers2.3 accelerate2.4 deepspeed2.5 peft2.6 环境代码修改 3训练代码准备4 代码修改 三&#xff0c;训练后推理验证四.推理效果展示1.微调前2.微调后 前言 本期我们采用魔塔…

【高考志愿】医学

目录 一、明确职业定位与兴趣 二、选择大学与专业 三、考虑身体条件 四、了解录取规则 五、考虑选科与成绩 六、注意志愿填报策略 七、关注就业前景 八、资深医生的建议 高考志愿填报学医时&#xff0c;考生需要综合考虑多个因素&#xff0c;确保自己能够做出明智的选择…

short s1 = 1; s1 = s1 + 1;有错吗

这个问题涉及到Java中的数据类型转换和赋值操作&#xff0c;是一个常见的面试题&#xff0c;用于考察应聘者对Java语言基础知识的掌握程度。 技术难点 数据类型转换&#xff1a;在Java中&#xff0c;基本数据类型之间的运算需要遵循类型转换规则。特别是当较小的数据类型&…

URLSearchParams: 浏览器中的查询字符串处理利器

一、 概述 在Web开发中&#xff0c;处理URL的查询字符串是一个常见任务。URLSearchParams API 提供了一种简单而强大的方法来处理Web URL的查询参数。它是一个内置的浏览器API&#xff0c;允许你以名称/值对的形式轻松地创建、读取、更新和删除查询参数。 二、URLSearchParam…

PostgreSQL的系统视图pg_stat_wal_receiver

PostgreSQL的系统视图pg_stat_wal_receiver 在 PostgreSQL 中&#xff0c;pg_stat_wal_receiver 视图提供了关于 WAL&#xff08;Write-Ahead Logging&#xff09;接收进程的统计信息。WAL 接收器是 PostgreSQL 集群中流复制的一部分&#xff0c;它在从节点中工作&#xff0c;…

distance delayed sound

distance delayed sound 在本章中&#xff0c;我们将讨论在游戏音频中使用距离延迟的重要性。我们将首先通过一个常见的例子——闪电和雷鸣&#xff0c;来展示这种重要性并解释距离延迟音频的基础知识。我们将讨论计算速度、距离和时间的数学和方程式&#xff0c;以确定距离延迟…

数据倾斜优化:Hive性能提升的核心

文章目录 1. 定义2. 数据倾斜2.1 Map2.2 Join2.3 Reduce 3. 写在最后 1. 定义 数据倾斜&#xff0c;也称为Data Skew&#xff0c;是在分布式计算环境中&#xff0c;由于数据分布不均匀导致某些任务处理的数据量远大于其他任务&#xff0c;从而形成性能瓶颈的现象。这种情况在H…

PotPlayer安装及高分辨率设置

第1步&#xff1a; 下载安装PotPlayer软件 PotPlayer链接&#xff1a;https://pan.baidu.com/s/1hW168dJrLBonUnpLI6F3qQ 提取码&#xff1a;z8xd 第2步&#xff1a; 下载插件&#xff0c;选择系统对应的位数进行运行&#xff0c;该文件不能删除&#xff0c;删除后将失效。 …

【强化学习的数学原理】课程笔记--2(贝尔曼最优公式,值迭代与策略迭代)

目录 贝尔曼最优公式最优 Policy求解贝尔曼最优公式求解最大 State Value v ∗ v^* v∗根据 v ∗ v^* v∗ 求解贪婪形式的最佳 Policy π ∗ \pi^* π∗一些证明过程 一些影响 π ∗ \pi^* π∗ 的因素如何让 π ∗ \pi^* π∗ 不 “绕弯路” γ \gamma γ 的影响reward 的…

2024/6/30周报

文章目录 摘要ABSTRACT文献阅读题目问题本文贡献方法LSTMTCN模型总体架构 实验实验结果 深度学习TCN-LSTM代码运行结果 总结 摘要 本周阅读了一篇关于TCN和LSTM进行光伏功率预测的文章&#xff0c;本文提出了一种利用LSTM-TCN预测光伏功率的新模型。它由长短期记忆和时间卷积网…

ThreadPoolExecutor基于ctl变量的声明周期管理

个人博客 ThreadPoolExecutor基于ctl变量的声明周期管理 | iwts’s blog 总集 想要完整了解下ThreadPoolExecutor&#xff1f;可以参考&#xff1a; 基于源码详解ThreadPoolExecutor实现原理 | iwts’s blog ctl字段的应用 线程池内部使用一个变量ctl维护两个值&#xff…

Vue异步DOM更新

Vue.js 是一个构建用户界面的渐进式框架&#xff0c;它采用了一种称为“响应式系统”的机制来跟踪数据的变化&#xff0c;并在数据变化时自动更新 DOM。然而&#xff0c;Vue 并不总是立即更新 DOM&#xff0c;而是会等待一个“tick”或“微任务”的完成&#xff0c;然后再进行批…

树莓派开发之文件传输

文章目录 一、简介使用U盘传输文件使用SD卡传输文件使用Xftp 7传输文件 二、 总结 一、简介 在树莓派开发中经常会用到文件传输&#xff0c;下面介绍几种树莓派文件传输的几种方法。 使用U盘传输文件 &#xff08;1&#xff09;复制所需传输文件到U盘 &#xff08;2&#…

C++:typeid4种cast转换

typeid typeid typeid是C标准库中提供的一种运算符&#xff0c;它用于获取类型的信息。它主要用于类型检查和动态类型识别。当你对一个变量或对象使用typeid运算符时&#xff0c;它会返回一个指向std::type_info类型的指针&#xff0c;这个信息包含了关于该类型名称、大小、基…

Pikachu靶场--Sql Inject

参考借鉴 pikachu靶场练习&#xff08;详细&#xff0c;完整&#xff0c;适合新手阅读&#xff09;-CSDN博客 数字型注入(post) 这种类型的SQL注入利用在用户输入处插入数值&#xff0c;而不是字符串。攻击者试图通过输入数字来修改SQL查询的逻辑&#xff0c;以执行恶意操作。…

如何在python中读取matlab输出的.mat文件

写在前面 经常会使用matlab处理数据&#xff0c;比如之前在进行空间滤波发现matlab自带的空间滤波函数。但是&#xff0c;由于后续使用python绘图更熟练一点&#xff0c;所以涉及到在python中读取matlab输出的文件&#xff0c;一般以.mat文件为主。下面测试了几种读取方式&…