cJSON代码解读

news2024/11/20 1:53:46

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.coloradmin.cn/o/1351513.html

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

相关文章

安装VS Code到AWS EC2 Linux 2

文章目录 小结问题及解决参考 小结 本文记录了安装VS Code到AWS EC2 Linux 2。 问题及解决 安装VS Code到AWS EC2 Linux 2采取了以下步骤&#xff1a; 更新VS Code的YUM源&#xff1a; [ec2-userip-100-92-28-119 ~]$ sudo rpm --import https://packages.microsoft.com/k…

LOG滤波器原理探究---计算机视觉和特征检测

先来看几个滤波器公式&#xff1a; 高斯滤波器&#xff1a; G ( x , y ; σ ) 1 2 π σ 2 e − x 2 y 2 2 σ 2 G(x,y;\sigma) \frac{1}{2 \pi \sigma^2} e^{-\frac{x^2 y^2}{2\sigma^2}} G(x,y;σ)2πσ21​e−2σ2x2y2​ 图像的二阶导数&#xff1a; ∇ 2 f ∂ 2 f ∂…

案例086:基于微信小程序的影院选座系统

文末获取源码 开发语言&#xff1a;Java 框架&#xff1a;SSM JDK版本&#xff1a;JDK1.8 数据库&#xff1a;mysql 5.7 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven3.5.4 小程序框架&#xff1a;uniapp 小程序开发软件&#xff1a;HBuilder X 小程序…

CTF流量分析经典例题详解

目录 入门题型 题目&#xff1a;Cephalopod(图片提取) 题目&#xff1a;特殊后门(icmp协议信息传输) 题目&#xff1a;手机热点(蓝牙传输协议obex,数据提取) 题目&#xff1a;想蹭网先解开密码(无线密码破解) 进阶题型 题目&#xff1a;抓到一只苍蝇(数据包筛选,数据提…

JAVAEE初阶相关内容第二十弹--HTTP协议

写在前&#xff1a;2024年啦&#xff01;新的一年要努力学习啦 本篇博客围绕HTTP协议&#xff0c;对HTTP协议进行了解&#xff0c;需要理解其工作过程&#xff0c;对HTTP协议格式要清楚&#xff0c;通过抓包工具进行协议分析&#xff0c;认识“方法”、“请求报头”&#xff0c…

什么是安全信息和事件管理(SIEM),有什么用处

安全信息和事件管理&#xff08;SIEM&#xff09;对于企业主动识别、管理和消除安全威胁至关重要。SIEM 解决方案采用事件关联、AI 驱动的异常检测以及机器学习驱动的用户和实体行为分析 &#xff08;UEBA&#xff09; 等机制来检测、审查和应对网络安全威胁。这些功能使 SIEM …

OpenCV图像处理——C++实现亚像素尺寸标定板边缘轮廓提取

前言 标定模板&#xff08;Calibration Target&#xff09;在机器视觉、图像测量、摄影测量以及三维重建等应用中起着重要的作用。它被用于校正相机的畸变&#xff0c;确定物理尺寸和像素之间的换算关系&#xff0c;并建立相机成像的几何模型。通过使用相机拍摄带有固定间距图…

基于simiulink的flyback反激型电路建模与仿真

目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 4.1 Flyback反激型电路的基本原理 4.2 Flyback反激型电路的数学建模 4.3 Flyback反激型电路的仿真方法 5.完整工程文件 1.课题概述 flyback反激型电路建模与仿真。反激变换器在开关管导通时电源将电能…

IDEA2023 最新版详细图文安装教程(安装+运行测试+汉化+背景图设置)

IDEA2023 最新版详细图文安装教程 名人说&#xff1a;工欲善其事&#xff0c;必先利其器。——《论语》 作者&#xff1a;Code_流苏(CSDN) o(‐&#xff3e;▽&#xff3e;‐)o很高兴你打开了这篇博客&#xff0c;跟着教程去一步步尝试安装吧。 目录 IDEA2023 最新版详细图文安…

LiveGBS流媒体平台GB/T28181常见问题-如何配置快照目录快照存储默认目录目录如何配置

LiveGBS流媒体平台GB/T28181常见问题-如何配置快照目录快照存储默认目录目录如何配置 1、快照目录2、指定快照目录3、搭建GB28181视频直播平台 1、快照目录 部署LiveGBS后&#xff0c; 再查看通道播放后 或是 获取通道快照后&#xff0c;就会在LiveSMS部署的服务器里面存储对应…

大文件断点下载Range下载zip包显示文件损坏

问题&#xff1a;大文件下载&#xff0c;其它格式的文件及rar格式的压缩包正常下载但是 之后zip包下载后解压失败 原因分析: 1. 查看上传文件的属性值 如图&#xff0c;10.4kb是已经约去小数点的值&#xff0c;准确的大小应该是10663字节10.4130859375KB&#xff0c;所以用10.…

C#使用纯OpenCvSharp部署yolov8-pose姿态识别

【源码地址】 github地址&#xff1a;https://github.com/ultralytics/ultralytics 【算法介绍】 Yolov8-Pose算法是一种基于深度神经网络的目标检测算法&#xff0c;用于对人体姿势进行准确检测。该算法在Yolov8的基础上引入了姿势估计模块&#xff0c;通过联合检测和姿势…

解决Android Studio The path ‘X:\XXX‘ does not belong to a directory.

目录 前言 一、问题描述 二、解决方法 前言 在移动应用开发领域&#xff0c;Android Studio作为一款功能强大的集成开发环境&#xff0c;为开发人员提供了丰富的工具和功能。然而&#xff0c;在使用Android Studio的过程中&#xff0c;有时也会遇到各种各样的问题和错误。 &…

彻底理解前端安全面试题(2)—— CSRF 攻击,跨站请求伪造攻击详解,建议收藏(含源码)

前言 前端关于网络安全看似高深莫测&#xff0c;其实来来回回就那么点东西&#xff0c;我总结一下就是 3 1 4&#xff0c;3个用字母描述的【分别是 XSS、CSRF、CORS】 一个中间人攻击。当然 CORS 同源策略是为了防止攻击的安全策略&#xff0c;其他的都是网络攻击。除了这…

以 RoCE+软件定义存储同时实现信创转型与架构升级

目前&#xff0c;不少企业数据中心使用 FC 交换机和集中式 SAN 存储&#xff08;以下简称“FC-SAN 架构”&#xff09;&#xff0c;支持核心业务系统、数据库、AI/ML 等高性能业务场景。而在开展 IT 基础架构信创转型时&#xff0c;很多用户受限于国外交换机&#xff1a;FC 交换…

2015年AMC8数学竞赛中英文真题典型考题、考点分析和答案解析

今天我们来看2015年的AMC8竞赛真题的典型考题和解析&#xff0c;最后利用碎片化时间冲刺&#xff0c;查漏补缺&#xff0c;理解考试。温馨提示&#xff1a;2024年AMC8比赛现在还可以报名&#xff0c;自由报名截止到1月7日&#xff0c;我这里有官方自由报名通道。 2015年AMC8数学…

对基于深度神经网络的Auto Encoder用于异常检测的一些思考

一、前言 现实中&#xff0c;大部分数据都是无标签的&#xff0c;人和动物多数情况下都是通过无监督学习获取概念&#xff0c;故而无监督学习拥有广阔的业务场景。举几个场景&#xff1a;网络流量是正常流量还是攻击流量、视频中的人的行为是否正常、运维中服务器状态是否异常…

Django Web框架

1、创建PyCharm项目 2、安装框架 pip install django4.2.0 3、查看安装的包列表 4、使用命令创建django项目 django-admin startproject web 5、目录结构 6、运行 cd web python manage.py runserver7、初始化后台登录的用户名密码 执行数据库迁移生成数据表 python man…

自定义指令:让 Vue 开发更有趣(中)

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

一年中ChatGPT使用情况

介绍 本人是独立开源软件开发者&#xff0c;参与很多项目建设&#xff0c;谈下日常使用情况。 我用了一年多&#xff0c;现在已经离不开&#xff0c;我如指挥家&#xff0c;它是我最忠诚的乐手。 编码 GitHub Copilot&#xff1a;GitHub Copilot是GitHub和OpenAI合作开发的一…