文章目录
- 前言
- 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_AddItemToObject
和cJSON_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_key
为true
,那么在添加新项目到JSON对象时,键(key)将会进行浅拷贝,也就是直接复制指针。这意味着,如果原始的键字符串在以后被修改或释放,那么存储在JSON对象中的键也会受到影响。
如果constant_key
为false
,那么键(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;
}
}
他的图例:
- 如果链表为空 (
child == NULL
),那么新的item
就会成为链表的第一个(也是唯一的)元素。这个情况可以用下面的字符画来表示:
array
|
v
item --> NULL
^
|
item
在这个图中,array->child
指向item
,item->prev
指向item
自身,item->next
指向NULL
。
- 如果链表不为空,那么新的
item
就会被添加到链表的末尾。这个情况可以用下面的字符画来表示:
array
|
v
child1 <--> child2 <--> ... <--> childN <--> item --> NULL
^ ^
| |
child1 item
在这个图中,array->child
指向链表的第一个元素child1
,childN->next
指向新的item
,item->prev
指向childN
,item->next
指向NULL
。
这样就成功的把我们的item连接到object里面了
总结
通过深入研究add_item_to_object函数的源码,我们可以更好地理解cJSON库是如何处理JSON对象的。这个函数的实现虽然简单,但却非常关键,它使得我们可以方便地向JSON对象中添加新的项目。希望这篇文章能帮助你更好地理解cJSON库的内部工作原理,以及如何在你自己的项目中使用它。