cJSON代码解读

1、背景

cJSON用了很久,但是对它一直不太了解。这次向添加对long long类型的支持,一直出问题。因为有以前添加两位小数float的经历,我觉得会很轻松,没想到翻车了。于是有了这边文档,阅读了部分博主对cJSON的解析,给出自己的体悟。

1.1 参考文档

【万字详解】cJSON解析-CSDN博客

2 从使用者角度分析

2.1 数据结构上分析

cJSON在使用上来说有两种:

1、将json字符串输入得到key-value;

2、将key-value输入得到一个json字符串;

两者的桥梁就是cJSON提供结构体cJSON,由该结构体通过链表形成一个树来表征一个JSON。

一个cJSON结构体是对JSON数据的抽象。

  /* The cJSON structure: */typedef struct cJSON{/* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */struct cJSON *next;struct cJSON *prev;/* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */struct cJSON *child;/* The type of the item, as above. */int type;/* The item's string, if type==cJSON_String  and type == cJSON_Raw */char *valuestring;/* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */int valueint;/* The item's number, if type==cJSON_Number */double valuedouble;/* The item's number, if type==cJSON_Int64 */long long valueint64;/* The item's name string, if this item is the child of, or is in the list of subitems of an object. */char *string;} cJSON;

next、prev是链表的后继和前驱---兄弟节点;

child是子节点;

string 是该节点的key,而value 可以根据type类型来决定,是valuestring、valueint、valuedouble、valueint64。

 接下去就是从使用角度分析,分别是组包JSON和解析JSON字符串两个角度出发。

2.2组包JSON

涉及到的函数如下所述,由创建cJSON、添加子节点、转成字符串、删除cJSON等过程。

  /*创建节点*/CJSON_PUBLIC(cJSON *)cJSON_CreateArray(void);CJSON_PUBLIC(cJSON *)cJSON_CreateObject(void);/*添加子节点*/CJSON_PUBLIC(cJSON *)cJSON_AddNumberToObject(cJSON *const object, const char *const name, const double number);CJSON_PUBLIC(cJSON *)cJSON_AddDoubleToObject(cJSON *const object, const char *const name, const double number);CJSON_PUBLIC(cJSON *)cJSON_AddInt64ToObject(cJSON *const object, const char *const name, const long long number);CJSON_PUBLIC(cJSON *)cJSON_AddStringToObject(cJSON *const object, const char *const name, const char *const string);/*添加子节点2*/  CJSON_PUBLIC(void)cJSON_AddItemToArray(cJSON *array, cJSON *item);CJSON_PUBLIC(void)cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item);/*cJSON对象转字符串*/   /* Render a cJSON entity to text for transfer/storage. */CJSON_PUBLIC(char *)cJSON_Print(const cJSON *item);CJSON_PUBLIC(void)cJSON_Minify(char *json);/*删除cJSON对象*/   /* Delete a cJSON entity and all subentities. */CJSON_PUBLIC(void)cJSON_Delete(cJSON *c);

每个过程看一两个函数实现

2.2.1 创建节点指定类型的节点

/* Internal constructor. */
static cJSON *cJSON_New_Item(const internal_hooks *const hooks)
{cJSON *node = (cJSON *)hooks->allocate(sizeof(cJSON));if (node){memset(node, '\0', sizeof(cJSON));}return node;
}
CJSON_PUBLIC(cJSON *)
cJSON_CreateObject(void)
{cJSON *item = cJSON_New_Item(&global_hooks);if (item){item->type = cJSON_Object;}return item;
}

 申请一个新的节点,成功则将类型设置成创建的类型,返回该节点。

这里涉及到一个类型的说法

/* cJSON Types: */
#define cJSON_Invalid (0)            /*无实际意义值,初始化节点时状态*/
#define cJSON_False (1 << 0)        /*布尔类型false*/
#define cJSON_True (1 << 1)        /*布尔类型true*/
#define cJSON_NULL (1 << 2)        /*空类型NULL*/
#define cJSON_Number (1 << 3)        /*数值类型*/
#define cJSON_String (1 << 4)        /*字符串类型*/
#define cJSON_Array (1 << 5)        /*列表类型, child存储值*/
#define cJSON_Object (1 << 6)        /*对象类型, child存储值*/
#define cJSON_Raw (1 << 7) /* raw json 表示valuestring中以\0结尾字符数据的任何类型*/
#define cJSON_Double (1 << 8)         /*浮点类型*/   
#define cJSON_Int64 (1 << 9)        /*long long int类型*/ #define cJSON_Valid_Flags (0x03FF)
/*两个标志*/
#define cJSON_IsReference (512)     /*标记child指向或valuestring不属于该节点,无需释放*/
#define cJSON_StringIsConst (1 << 10)    /*string成员是一个常量,无需释放*/

如上述代码所述,用位来标记是什么类型的。

另外还有两个标志,分别

(1)表示该节点是个引用,其中的child和valuestring不属于该节点,释放时注意;

(2)表示该节点的string成员指向的是个常量,无需释放。

 2.2.2 删除节点

/* Delete a cJSON structure. */
CJSON_PUBLIC(void)
cJSON_Delete(cJSON *item)
{cJSON *next = NULL;while (item != NULL)    /*循环直至链表释放完*/{next = item->next;    /*链表下一个兄弟节点*/printf("item type 0x%x\n", item->type);/*节点不带引用标志,且有子节点,则释放子节点*/if (!(item->type & cJSON_IsReference) && (item->child != NULL)){cJSON_Delete(item->child);}/*节点不带引用标志,且有valuestring,则释放valuestring*/if (!(item->type & cJSON_IsReference) && (item->valuestring != NULL)){global_hooks.deallocate(item->valuestring);}/*节点不是常量标志,则释放string*/if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)){global_hooks.deallocate(item->string);}/*释放节点本身*/global_hooks.deallocate(item);item = next;    /*item=下一个兄弟节点*/}
}

 具体看代码注释

和创建节点中标志位一一对应。释放节点下子节点和一切动态分配的资源。

 2.2.3添加子节点1

/*
* @fn add_item_to_object
* @param object The object to add to.
* @param string The string in key.
* @param item The item to add.
* @param hooks The hooks to use.
* @param constant_key Whether the key is a constant or not.
* @return true on success, false on failure.
*/
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)
{if ((object == NULL) || (string == NULL) || (item == NULL)){return false;}if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)){hooks->deallocate(item->string);}if (constant_key){item->string = (char *)cast_away_const(string);item->type |= cJSON_StringIsConst;}else{char *key = (char *)cJSON_strdup((const unsigned char *)string, hooks);if (key == NULL){return false;}item->string = key;item->type &= ~cJSON_StringIsConst;}return add_item_to_array(object, item);
}static cJSON_bool add_item_to_array(cJSON *array, cJSON *item)
{cJSON *child = NULL;if ((item == NULL) || (array == NULL)){return false;}child = array->child;if (child == NULL){/* list is empty, start new one */array->child = item;}else{/* append to the end */while (child->next){child = child->next;}suffix_object(child, item);}return true;
}/*
* @fn cJSON_AddStringToObject
* @param object The object to add to.
* @param name The name of the item to add.
* @param string The string to add.
* @return The new item, or NULL on failure.
*/
CJSON_PUBLIC(cJSON *)
cJSON_AddStringToObject(cJSON *const object, const char *const name, const char *const string)
{cJSON *string_item = cJSON_CreateString(string);if (add_item_to_object(object, name, string_item, &global_hooks, false)){return string_item;}cJSON_Delete(string_item);return NULL;
}

cJSON_AddStringToObject函数向object节点中添加key是name, value是string的子节点,先将value复制给了valuestring成员;

在add_item_to_object根据constant_key的值,给key赋了值,对type成员设置关于cJSON_StringIsConst标志的值;

add_item_to_array将子节点添加在父节点的child成员指向的链表下

2.2.4 添加子节点2

CJSON_PUBLIC(void)
cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item)
{add_item_to_object(object, string, item, &global_hooks, false);
}

也调用了add_item_to_object,不再叙述

2.2.5 cJSON结构体转字符串

static unsigned char *print(const cJSON *const item, cJSON_bool format, const internal_hooks *const hooks)
{static const size_t default_buffer_size = 256;printbuffer buffer[1];unsigned char *printed = NULL;memset(buffer, 0, sizeof(buffer));/* create buffer */buffer->buffer = (unsigned char *)hooks->allocate(default_buffer_size);buffer->length = default_buffer_size;buffer->format = format;buffer->hooks = *hooks;if (buffer->buffer == NULL){goto fail;}/* print the value */if (!print_value(item, buffer)){goto fail;}update_offset(buffer);/* check if reallocate is available */if (hooks->reallocate != NULL){printed = (unsigned char *)hooks->reallocate(buffer->buffer, buffer->offset + 1);buffer->buffer = NULL;if (printed == NULL){goto fail;}}else /* otherwise copy the JSON over to a new buffer */{printed = (unsigned char *)hooks->allocate(buffer->offset + 1);if (printed == NULL){goto fail;}memcpy(printed, buffer->buffer, cjson_min(buffer->length, buffer->offset + 1));printed[buffer->offset] = '\0'; /* just to be sure *//* free the buffer */hooks->deallocate(buffer->buffer);}return printed;fail:if (buffer->buffer != NULL){hooks->deallocate(buffer->buffer);}if (printed != NULL){hooks->deallocate(printed);}return NULL;
}
/* Render a cJSON item/entity/structure to text. */
CJSON_PUBLIC(char *)
cJSON_Print(const cJSON *item)
{return (char *)print(item, true, &global_hooks);
}

 简单就是cJSON_Print

        ->print

                ->print_value

                ->realloc

                ->return printed;

核心看print_value


/* Render a value to text. */
static cJSON_bool print_value(const cJSON *const item, printbuffer *const output_buffer)
{unsigned char *output = NULL;/*检查输入和输出参数*/if ((item == NULL) || (output_buffer == NULL)){return false;}/*排除标志影响,直接看item是什么类型的*/switch ((item->type) & cJSON_Valid_Flags){case cJSON_NULL:output = ensure(output_buffer, 5);if (output == NULL){return false;}strcpy((char *)output, "null");return true;case cJSON_False:output = ensure(output_buffer, 6);if (output == NULL){return false;}strcpy((char *)output, "false");return true;case cJSON_True:output = ensure(output_buffer, 5);if (output == NULL){return false;}strcpy((char *)output, "true");return true;case cJSON_Number:return print_number(item, output_buffer);case cJSON_Double:return print_double(item, output_buffer);case cJSON_Int64:return print_int64(item, output_buffer);case cJSON_Raw:{size_t raw_length = 0;if (item->valuestring == NULL){if (!output_buffer->noalloc){output_buffer->hooks.deallocate(output_buffer->buffer);}return false;}raw_length = strlen(item->valuestring) + sizeof("");output = ensure(output_buffer, raw_length);if (output == NULL){return false;}memcpy(output, item->valuestring, raw_length);return true;}case cJSON_String:return print_string(item, output_buffer);case cJSON_Array:return print_array(item, output_buffer);case cJSON_Object:return print_object(item, output_buffer);default:return false;}
}

 根据cJSON对象所述类型进行字符串化,我们关注一个object、array、num这三种类型,string类型用脚趾都能想到。

2.2.5.1 print_object
/* Render an object to text. */
static cJSON_bool print_object(const cJSON *const item, printbuffer *const output_buffer)
{unsigned char *output_pointer = NULL;size_t length = 0;cJSON *current_item = item->child;if (output_buffer == NULL){return false;}/* Compose the output: */length = (size_t)(output_buffer->format ? 2 : 1); /* fmt: {\n */output_pointer = ensure(output_buffer, length + 1);if (output_pointer == NULL){return false;}/*花括号前部分*/*output_pointer++ = '{';output_buffer->depth++;if (output_buffer->format){*output_pointer++ = '\n';}output_buffer->offset += length;while (current_item){if (output_buffer->format){size_t i;output_pointer = ensure(output_buffer, output_buffer->depth);if (output_pointer == NULL){return false;}for (i = 0; i < output_buffer->depth; i++){*output_pointer++ = '\t';}output_buffer->offset += output_buffer->depth;}/*子节点的key*//* print key */if (!print_string_ptr((unsigned char *)current_item->string, output_buffer)){return false;}update_offset(output_buffer);length = (size_t)(output_buffer->format ? 2 : 1);output_pointer = ensure(output_buffer, length);if (output_pointer == NULL){return false;}/*key-value分隔符*/*output_pointer++ = ':';if (output_buffer->format){*output_pointer++ = '\t';}output_buffer->offset += length;/*子节点value,调用print_value*//* print value */if (!print_value(current_item, output_buffer)){return false;}update_offset(output_buffer);/* print comma if not last */length = (size_t)((output_buffer->format ? 1 : 0) + (current_item->next ? 1 : 0));output_pointer = ensure(output_buffer, length + 1);if (output_pointer == NULL){return false;}if (current_item->next){/*如果子节点有下一个兄弟节点,加逗号*//* print value */*output_pointer++ = ',';}if (output_buffer->format){*output_pointer++ = '\n';}*output_pointer = '\0';output_buffer->offset += length;current_item = current_item->next;}output_pointer = ensure(output_buffer, output_buffer->format ? (output_buffer->depth + 1) : 2);if (output_pointer == NULL){return false;}if (output_buffer->format){size_t i;for (i = 0; i < (output_buffer->depth - 1); i++){*output_pointer++ = '\t';}}/*花括号后部分*/*output_pointer++ = '}';*output_pointer = '\0';output_buffer->depth--;return true;
}

 object类型的cJSON对象输出字符串类似下图,具体格式细节还是根据format来控制

{
"child_key":"child_value"
}

format为true时,配合\t和深度来缩进完成格式化。

2.2.5.2 print_array
/* Render an array to text */
static cJSON_bool print_array(const cJSON *const item, printbuffer *const output_buffer)
{unsigned char *output_pointer = NULL;size_t length = 0;cJSON *current_element = item->child;if (output_buffer == NULL){return false;}/* Compose the output array. *//* opening square bracket */output_pointer = ensure(output_buffer, 1);if (output_pointer == NULL){return false;}/*列表中括号前部分*/*output_pointer = '[';output_buffer->offset++;output_buffer->depth++;while (current_element != NULL){    /*子节点的value*/if (!print_value(current_element, output_buffer)){return false;}update_offset(output_buffer);if (current_element->next){length = (size_t)(output_buffer->format ? 2 : 1);output_pointer = ensure(output_buffer, length + 1);if (output_pointer == NULL){return false;}/*有下一个对象,则添加列表对象间分隔符*/*output_pointer++ = ',';if (output_buffer->format){*output_pointer++ = ' ';}*output_pointer = '\0';output_buffer->offset += length;}current_element = current_element->next;}output_pointer = ensure(output_buffer, 2);if (output_pointer == NULL){return false;}/*中括号后部分*/*output_pointer++ = ']';*output_pointer = '\0';output_buffer->depth--;return true;
}

输出为

[子节点值,子节点值]

 这都是从结果来推到需求,写代码时从需求到结果,其实更好的分析方法是想自己该如何实现它。

2.2.5.3 print_number
/* Render the number nicely from the given item into a string. */
static cJSON_bool print_number(const cJSON *const item, printbuffer *const output_buffer)
{unsigned char *output_pointer = NULL;double d = item->valuedouble;int length = 0;size_t i = 0;unsigned char number_buffer[26]; /* temporary buffer to print the number into */unsigned char decimal_point = get_decimal_point();double test;if (output_buffer == NULL){return false;}/* This checks for NaN and Infinity */if ((d * 0) != 0){length = sprintf((char *)number_buffer, "null");}else{/* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */length = sprintf((char *)number_buffer, "%1.15g", d);/* Check whether the original double can be recovered */if ((sscanf((char *)number_buffer, "%lg", &test) != 1) || ((double)test != d)){/* If not, print with 17 decimal places of precision */length = sprintf((char *)number_buffer, "%1.17g", d);}}/* sprintf failed or buffer overrun occured */if ((length < 0) || (length > (int)(sizeof(number_buffer) - 1))){return false;}/* reserve appropriate space in the output */output_pointer = ensure(output_buffer, (size_t)length + sizeof(""));if (output_pointer == NULL){return false;}/* copy the printed number to the output and replace locale* dependent decimal point with '.' */for (i = 0; i < ((size_t)length); i++){if (number_buffer[i] == decimal_point){output_pointer[i] = '.';continue;}output_pointer[i] = number_buffer[i];}output_pointer[i] = '\0';output_buffer->offset += (size_t)i;return true;
}

 %1.15g

  • %:格式化输出的开始符号
  • 1.15:表示输出的总宽度为1,小数点后保留15位有效数字
  • g:以指数形式输出浮点数

 按上述获取长度后,从double类型的valuedouble中转化为字符串

2.3 解析JSON

涉及到函数有

  /*解析*/CJSON_PUBLIC(cJSON *)cJSON_Parse(const char *value);/*获取节点数目或节点*/CJSON_PUBLIC(int)cJSON_GetArraySize(const cJSON *array);/* Retrieve item number "item" from array "array". Returns NULL if unsuccessful. */CJSON_PUBLIC(cJSON *)cJSON_GetArrayItem(const cJSON *array, int index);/* Get item "string" from object. Case insensitive. */CJSON_PUBLIC(cJSON *)cJSON_GetObjectItem(const cJSON *const object, const char *const string);

 2.3.1 解析

/* Parse an object - create a new root, and populate. */
CJSON_PUBLIC(cJSON *)
cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated)
{parse_buffer buffer = {0, 0, 0, 0, {0, 0, 0}};cJSON *item = NULL;/* reset error position */global_error.json = NULL;global_error.position = 0;if (value == NULL){goto fail;}buffer.content = (const unsigned char *)value;buffer.length = strlen((const char *)value) + sizeof("");buffer.offset = 0;buffer.hooks = global_hooks;/*创建根节点*/item = cJSON_New_Item(&global_hooks);if (item == NULL) /* memory fail */{goto fail;}if (!parse_value(item, buffer_skip_whitespace(skip_utf8_bom(&buffer)))){/* parse failure. ep is set. */goto fail;}/* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */if (require_null_terminated){buffer_skip_whitespace(&buffer);if ((buffer.offset >= buffer.length) || buffer_at_offset(&buffer)[0] != '\0'){goto fail;}}if (return_parse_end){*return_parse_end = (const char *)buffer_at_offset(&buffer);}return item;fail:if (item != NULL){cJSON_Delete(item);}if (value != NULL){error local_error;local_error.json = (const unsigned char *)value;local_error.position = 0;if (buffer.offset < buffer.length){local_error.position = buffer.offset;}else if (buffer.length > 0){local_error.position = buffer.length - 1;}if (return_parse_end != NULL){*return_parse_end = (const char *)local_error.json + local_error.position;}global_error = local_error;}return NULL;
}CJSON_PUBLIC(cJSON *)
cJSON_Parse(const char *value)
{return cJSON_ParseWithOpts(value, 0, 0);
}

 核心还是来到了parse_value

/* Parser core - when encountering text, process appropriately. */
static cJSON_bool parse_value(cJSON *const item, parse_buffer *const input_buffer)
{if ((input_buffer == NULL) || (input_buffer->content == NULL)){return false; /* no input */}/* parse the different types of values *//* null */if (can_read(input_buffer, 4) && (strncmp((const char *)buffer_at_offset(input_buffer), "null", 4) == 0)){item->type = cJSON_NULL;input_buffer->offset += 4;return true;}/* false */if (can_read(input_buffer, 5) && (strncmp((const char *)buffer_at_offset(input_buffer), "false", 5) == 0)){item->type = cJSON_False;input_buffer->offset += 5;return true;}/* true */if (can_read(input_buffer, 4) && (strncmp((const char *)buffer_at_offset(input_buffer), "true", 4) == 0)){item->type = cJSON_True;item->valueint = 1;input_buffer->offset += 4;return true;}/* string */if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '\"')){return parse_string(item, input_buffer);}/* number */if (can_access_at_index(input_buffer, 0) && ((buffer_at_offset(input_buffer)[0] == '-') || ((buffer_at_offset(input_buffer)[0] >= '0') && (buffer_at_offset(input_buffer)[0] <= '9')))){return parse_number(item, input_buffer);}/* array */if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '[')){return parse_array(item, input_buffer);}/* object */if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '{')){return parse_object(item, input_buffer);}return false;
}

 针对各自类型的特点开始解析,根据这里也没有啥特殊的,最终都化为字符串和数值。

2.3.2 获取节点数目

获取子节点的个数,子节点链表中对象数目。

2.3.3 获取节点

只能获取子节点的信息,采取一级一级剥洋葱的方式。

3、添加对long long类型的支持

有博主是在cJSON_NUM类型下开了个子类型,逻辑也是相当清晰,本文在大的类型里添加。

没有啥难度,照葫芦画瓢,把类型和标志的意义搞懂就没出过BUG了。

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

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

相关文章

02--数据定义语言DDL

1、数据定义语言DDL 1.1 操作数据库-DDL 创建数据库 create database 数据库名称; 创建数据库&#xff0c;并指定字符集 create database 数据库名称 character set 字符集名; 查询所有数据库的名称 show databases; 查询某个数据库的字符集:查询某个数据库的创建语句及字…

SpringBoot: 通过MyBatis访问ClickHouse

一、ClickHouse中建表&#xff0c;添加数据 二、SpringBoot项目添加mybatis、clickhouse、druid相关依赖 <dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.2.6</version></dependency>…

stm32 HAL库 4096线ABZ编码器

[TOC]目录 ABZ编码器 4096线 买的是这个 AB相代表计数方向&#xff0c;Z代表过零点 cubemx配置 定时器Encoder 也可以选上DMA 中断 Z相GPIO中断 找一个空闲管脚 打开对应中断 代码 不用DMA int main(void) {short Enc_cnt 0;HAL_TIM_Encoder_Start_IT(&ht…

CSS 向上扩展动画

上干货 <template><!-- mouseenter"startAnimation" 表示在鼠标进入元素时触发 startAnimation 方法。mouseleave"stopAnimation" 表示在鼠标离开元素时触发 stopAnimation 方法。 --><!-- 容器元素 --><div class"container&q…

逻辑回归(LR)----机器学习

基本原理 逻辑回归&#xff08;Logistic Regression&#xff0c;LR&#xff09;也称为"对数几率回归"&#xff0c;又称为"逻辑斯谛"回归。 logistic回归又称logistic 回归分析 &#xff0c;是一种广义的线性回归分析模型&#xff0c;常用于数据挖掘&#…

五、Spring AOP面向切面编程(基于注解方式实现和细节)

本章概要 Spring AOP底层技术组成初步实现获取通知细节信息切点表达式语法重用&#xff08;提取&#xff09;切点表达式环绕通知切面优先级设置CGLib动态代理生效注解实现小结 5.5.1 Spring AOP 底层技术组成 动态代理&#xff08;InvocationHandler&#xff09;&#xff1a;…

改变传媒格局的新趋势

在如今信息高速发展的时代&#xff0c;人们早已进入了一个以手机为中心的智能化时代。随着科技的迅猛发展&#xff0c;手机无人直播成为了一种新兴的传媒形态&#xff0c;正逐渐改变着传媒格局。本文将从手机无人直播的定义、发展背景和影响等方面进行探讨。 首先&#xff0c;…

关于我花费六千多组了台window+Linux主机

一边学习&#xff0c;一边总结&#xff0c;一边分享&#xff01; 写在前面 我在2023年12月组了一台“洋垃圾”的主机&#xff0c;一边当做台式机使用&#xff0c;一边当做服务器使用。这个方案算是相对比较划算的方案。我开始是打算直接单做服务器使用的&#xff0c;以及内存…

ROS学习笔记(7)进一步深入了解ROS第一步

0.前提 最近在学习宾夕法尼亚大学工程学院的ROS公开课&#xff0c;在尽力的去融入全英语的环境&#xff08;哪怕我的英语水准并不是很高&#xff09;。既然是在学习&#xff0c;笔记也就是必须的了&#xff0c;当然这些笔记都是课程当中提出的问题&#xff0c;我去寻找后得出的…

【STM32】I2C通信

基本的任务是&#xff1a;通过通信线&#xff0c;实现单片机读写外挂模块寄存器的功能。其中至少要实现在指定位置写寄存器和在指定的位置读寄存器这两个功能。 异步时序的优点&#xff1a;省一根时钟线&#xff0c;节约资源&#xff1b;缺点&#xff1a;对事件要求严格&#…

20231228在Firefly的AIO-3399J开发板的Android11的挖掘机的DTS配置单前置摄像头ov13850

20231228在Firefly的AIO-3399J开发板的Android11的挖掘机的DTS配置单前置摄像头ov13850 2023/12/28 10:42 【碰到一个很神奇的问题】&#xff1a; 昨天晚上前置摄像头怎么也点不亮&#xff01;改了巨多的地方&#xff01;晚上睡觉之前把开发板彻底断电了&#xff01;今天开电脑…

深度生成模型之GAN基础 ->(个人学习记录笔记)

文章目录 深度生成模型之GAN基础生成对抗网络1. 生成对抗网络如何生成数据2. 生成对抗原理3. GAN的核心优化目标4. D的优化5. GAN的理想状态6. GAN的训练7. 梯度不稳定与模式崩塌(collapse mode)问题8. 梯度消失问题 深度生成模型之GAN基础 生成对抗网络 1. 生成对抗网络如何…

深入研究矫正单应性矩阵用于立体相机在线自标定

文章&#xff1a;Dive Deeper into Rectifying Homography for Stereo Camera Online Self-Calibration 作者&#xff1a;Hongbo Zhao, Yikang Zhang, Qijun Chen,, and Rui Fan 编辑&#xff1a;点云PCL 欢迎各位加入知识星球&#xff0c;获取PDF论文&#xff0c;欢迎转发朋…

【Java并发】深入浅出 synchronized关键词原理-上

一个问题的思考 建设我们有两个线程&#xff0c;一个进行5000次的相加操作&#xff0c;另一个进行5000次的减操作。那么最终结果是多少 package com.jia.syn;import java.util.concurrent.TimeUnit;/*** author qxlx* date 2024/1/2 10:08 PM*/ public class SynTest {privat…

稳部落 – 新浪微博备份导出工具

稳部落 稳部落是新浪微博备份导出工具&#xff0c;可以帮助用户非常方便的导出备份新浪微博的数据&#xff0c;让我们可以永久保存这些微博数据。它支持新浪微博、微博私信、微博评论的导出&#xff0c;并可以备份包含图片、视频的完整微博内容。用户只需登录微博账号&#xf…

Java学习,一文掌握Java之SpringBoot框架学习文集(2)

&#x1f3c6;作者简介&#xff0c;普修罗双战士&#xff0c;一直追求不断学习和成长&#xff0c;在技术的道路上持续探索和实践。 &#x1f3c6;多年互联网行业从业经验&#xff0c;历任核心研发工程师&#xff0c;项目技术负责人。 &#x1f389;欢迎 &#x1f44d;点赞✍评论…

【计算机毕业设计】SSM游戏点评网站

项目介绍 本项目分为前后台&#xff0c;前台为普通用户登录&#xff0c;后台为管理员登录&#xff1b; 管理员角色包含以下功能&#xff1a; 管理员登录,管理员管理,网站用户管理,游戏资讯管理,游戏类型管理,城市信息管理,竞技场管理,游戏信息管理,游戏评价信息管理等功能。…

双侧电源系统距离保护MATLAB仿真模型

微❤关注“电气仔推送”获得资料&#xff08;专享优惠&#xff09; 系统原始数据 双侧电源系统模型如图所示&#xff1a; 仿真模型搭建 将线路AB分成Line1和Line2&#xff0c;将线路BC分成Line3和Line4&#xff0c;用三相电压电流测量模块作为系统母线&#xff0c;根据系统已…

Java基础-----集合类(一)

文章目录 1.集合类简介2. 自定义集合类 1.集合类简介 集合和数组一样&#xff0c;都是用来存储多个数据的结构&#xff0c;也可以称作容器。 数组长度是不可变化的&#xff0c;一旦在初始化数组时指定了数组长度&#xff0c;这个长度就不可变。如果需要处理数量变化的数据&am…

Flutter 混合开发 - 动态下发 libflutter.so libapp.so

背景 最近在做包体积优化&#xff0c;在完成代码混淆、压缩&#xff0c;裁剪ndk支持架构&#xff0c;以及资源压缩&#xff08;如图片转webp、mp3压缩等&#xff09;后发现安装包的中占比较大的仍是 so 动态库依赖。 具体查看发现 libflutter.so 和 libapp.so 的体积是最大的&…