好用的库代码简析

一、cJson库

1. 功能简述

  1. 处理json数据,支持创建、解析以及操作json数据,适用于嵌入式系统
  2. 特点:轻量、易用、跨平台
  3. 代码地址https://github.com/DaveGamble/cJSON.git,使用时只需要包含cJSON.ccJSON.h

2. API介绍和使用

cJSON* cJSON_CreateObject(void);    // 创建一个空的cJSON对象
cJSON* cJSON_CreateArray(void);    // 创建一个空的cJSON数组
cJSON* cJSON_CreateString(const char *string);    // 创建一个字符串cJSON对象
cJSON* cJSON_CreateNumber(double num);            // 创建一个数字cJSON对象
cJSON_bool cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item);    // 
cJSON* cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string);  // 

3. 使用示例

// 创建JSON示例
cJSON *root = cJSON_CreateObject();                // 创建一个根节点
cJSON_AddStringToObject(root, "name", "John");     // 添加字符串对象到根节点中
cJSON_AddNumberToObject(root, "age", 30);          // 添加数字对象到根节点中
cJSON *hobbies = cJSON_AddArrayToObject(root, "hobbies");  // 添加数组对象到根节点中
if (hobbies != NULL)
{
    cJSON_AddItemToArray(hobbies, cJSON_CreateString("reading"));     // 往cJSON节点中添加字符串属性
    cJSON_AddItemToArray(hobbies, cJSON_CreateString("swimming"));    // 往cJSON节点中添加字符串属性
    cJSON_AddItemToArray(hobbies, cJSON_CreateString("coding"));      // 往cJSON节点中添加字符串属性
}
cJSON *friends = cJSON_AddArrayToObject(root, "friends");      // 添加数组对象到根节点中
if (friends != NULL)
{
    // 第一个朋友
    cJSON *friend1 = cJSON_CreateObject();
    cJSON_AddStringToObject(friend1, "name", "Alice");
    cJSON_AddNumberToObject(friend1, "age", 28);
    cJSON_AddItemToArray(friends, friend1);                    // 嵌套添加节点
    
    // 第二个朋友
    cJSON *friend2 = cJSON_CreateObject();
    cJSON_AddStringToObject(friend2, "name", "Bob");
    cJSON_AddNumberToObject(friend2, "age", 32);
    cJSON_AddItemToArray(friends, friend2);                    // 嵌套添加节点
}
char *json_string = cJSON_Print(root);             // 生成一个格式化的字符串带空格
printf("%s\n", json_string);
cJSON_free(json_string);                           // 对应cJSON_Print的释放函数
char *json_string1 = cJSON_PrintUnformatted(root);             // 生成一个紧凑的字符串不带空格
printf("%s\n", json_string1);
cJSON_free(json_string1);                           // 对应cJSON_PrintUnformatted的释放函数
cJSON_Delete(root);                                // 对应cJSON_CreateObject的释放函数
// 解析JSON示例
cJSON *json = cJSON_Parse(json_string);            // 输入json字符串生成cJSON根节点
if (root == NULL) {                                // 错误检查
    printf("解析错误\n");
    return;
}
cJSON *name = cJSON_GetObjectItemCaseSensitive(json, "name");  // 从cJSON节点中根据名称获得对应的cJSON对象
if (name != NULL && cJSON_IsString(name))
{
    const char *name_str = cJSON_GetStringValue(name);        // 处理字符串
    printf("姓名: %s\n", name_str);
}
cJSON_Delete(json);                                // 释放cJSON根节点

4. 主要数据结构与API实现方式

// 关键结构体
typedef struct cJSON
{
    struct cJSON *next;
    struct cJSON *prev;      // 双向链表组织cJSON结构体
    struct cJSON *child;     // json结构的子节点
    int type;                // json对象的类型
    char *valuestring;       // 字符串,类型是cJSON_String或cJSON_Raw时该值有效
    int valueint;            // 数值,整数值,被valuedouble替代
    double valuedouble;      // 数值,浮点数,类型是cJSON_Number时该值有效
    char *string;            // 名称
} cJSON;
// 内存分配和释放函数
malloc、free、realloc分别对应global_hooks结构体的三个成员
void cJSON_InitHooks(cJSON_Hooks* hooks);    // 该函数可以自定义内存的分配和释放函数
cJSON *cJSON_New_Item(const internal_hooks * const hooks);    // 该函数调用malloc函数返回一个cJSON的结构体并返回指针
unsigned char* cJSON_strdup(const unsigned char* string, const internal_hooks * const hooks);  // 字符串复制函数,调用内存分配并memcpy
cJSON *cJSON_CreateObject(void);      // 调用cJSON_New_Item获得一个新的cJSON对象,并设置type为cJSON_Object
cJSON *cJSON_CreateArray(void);       // 调用cJSON_New_Item获得一个新的cJSON对象,并设置type为cJSON_Array
cJSON *cJSON_CreateNumber(double num);  // 调用cJSON_New_Item获得一个新的cJSON对象,并设置type为cJSON_Number,并设置valuedouble成员
cJSON *cJSON_CreateRaw(const char *raw);  // 调用cJSON_New_Item获得一个新的cJSON对象,并设置type为cJSON_Raw,并调用cJSON_strdup完成字符串
cJSON *cJSON_CreateString(const char *string);  // 同上,区别是type是cJSON_String
cJSON *cJSON_CreateIntArray(const int *numbers, int count);  // 数组表示一个节点,child成员是第一个数字节点,所有的数字节点使用双向链表连接
cJSON *cJSON_CreateFloatArray(const float *numbers, int count);  // 同上
cJSON *cJSON_CreateDoubleArray(const double *numbers, int count);  // 同上
cJSON *cJSON_CreateStringArray(const char *const *strings, int count);  // 同上
cJSON_bool cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item);  // 添加元素到cJSON对象中
cJSON_bool cJSON_AddItemToArray(cJSON *array, cJSON *item);    // 添加元素到数组类型的cJSON对象中
cJSON_AddxxToObject;            //创建一个xx,并将xx放入Object中
char *cJSON_Print(const cJSON *item);  // 格式化输出json为字符串
char *JSON_PrintUnformatted(const cJSON *item);    // 压缩输出json为字符串
cJSON *cJSON_Parse(const char *value);    // 输入json字符串,输出JSON根节点
cJSON *name = cJSON_GetObjectItemCaseSensitive(json, "name");  // 从cJSON节点中根据名称获得对应的cJSON对象
cJSON_IsString是否是string
cJSON_IsNumber是否是number
cJSON_IsBool是否是布尔
cJSON_IsArray是否是数组,之后使用宏cJSON_ArrayForEach进行范围for循环,实际是一个for循环的封装
cJSON_GetStringValue返回json的字符串,类型不匹配返回NULL
cJSON_GetNumberValue返回json的数值,类型不匹配返回NAN

5. 需要关注的点

  1. cJSON_CreateRaw中的字符串是原始的,不进行转义
  2. cJSON_CreateIntArray创建了n+1个节点,其中一个节点作为父节点表示数组,其他的n个节点存储数据并通过双向链表连接
  3. cJSON_ArrayForEach看起来像是范围for循环,实际是for循环的封装
  4. 关注的有数值、字符串、数组、嵌套
  5. cJSON_CreateObjectcJSON_Delete对应
  6. 使用cJSON_PrintcJSON_PrintUnformatted的返回值需要使用cJSON_free释放
  7. 使用cJSON_AddxxToObject将json节点添加到Object,xx节点就不需要自己释放,释放Object的时候会释放掉xx
  8. cJSON_ParsecJSON_Delete对应

二、来自Zephyr的SMF

1. 概述

  1. 核心文件smf.hsmf.c以及单独拿出这两个文件需要的配置文件,实现了简单的状态机
  2. 状态:每个状态有三个函数,entryrunexit分别对应状态进入、状态执行和状态退出执行的函数
  3. 状态转换发生的时候,SMF会自动调用响应状态的entryexit函数
  4. 结构体smf_ctx保存了状态机的运行状态,例如当前状态、先前状态和终止标志
  5. entry函数进入状态触发,执行一次,做初始化和资源分配操作
  6. run函数,调用smf_run_state会执行,可以执行多次,用于处理事件、检查条件和执行状态内逻辑操作
  7. exit函数离开状态触发,执行一次,用作清理资源、保存状态
  8. 使用时,一般将结构体smf_ctx放到自定义结构体的首位,这样使用宏SMF_CTX(o)可以将状态结构体中函数的传参obj强制转换为smf_ctx结构体指针

2. 关键结构体

// 状态结构体
struct smf_state {
    const state_method entry;      // 进入状态时执行的函数
    const state_execution run;     // 状态运行时执行的函数
    const state_method exit;       // 退出状态时执行的函数
    // ... 其他可选字段(层次状态机相关)
};
typedef void (*state_method)(void *obj);                  // 状态进入和退出函数参数是void*(一般是包含结构体smf_ctx的结构体),返回值void
typedef enum smf_state_result (*state_execution)(void *obj);  // 状态运行函数参数是void*(同上),返回值是一个枚举
enum smf_state_result 一种是SMF_EVENT_HANDLED(事件已处理不需要向上传播)另一种是SMF_EVENT_PROPAGATE(事件未处理需要向上传播)
SMF_CREATE_STATE(_entry, _run, _exit, _parent, _initial)      // 宏用来创建一个结构体smf_state,参数都可以是NULL
// 状态机上下文结构体
struct smf_ctx {
    const struct smf_state *current;    // 当前状态
    const struct smf_state *previous;   // 先前状态
    const struct smf_state *executing;  // 正在执行的状态(层次状态机)
    int32_t terminate_val;              // 终止值
    uint32_t internal;                  // 内部状态(框架使用)
};
// 宏
#define SMF_CTX(o) ((struct smf_ctx *)o)            // 实现了结构体指针的强制转换

3. API介绍

void smf_set_initial(struct smf_ctx *ctx, const struct smf_state *init_state);    // 初始化状态机并设置一个初始状态
该函数执行了初始状态的entry函数,并设置内部标志位,如果启用层次状态机,则自动处理初始化转换连
void smf_set_state(struct smf_ctx *ctx, const struct smf_state *new_state);      // 状态转换为new_state
该函数分三步,第一步递归执行从当前状态到公共祖先状态的exit函数,第二步更新状态机上下文的状态变量,第三步执行从公共祖先到目标状态的entry函数
int32_t smf_run_state(struct smf_ctx *ctx);        // 调用run函数进行事件处理
该函数调用当前状态的run函数,并且如果是层次状态机之后还会依次调用父对象的run函数
void smf_set_terminate(struct smf_ctx *ctx, int32_t val);      // 终止状态机,并设置一个终止值
该函数执行后再次执行smf_run_state会返回刚才设置的val值
const struct smf_state *smf_get_current_leaf_state(const struct smf_ctx *const ctx);    // 获得当前叶子状态
const struct smf_state *smf_get_current_executing_state(const struct smf_ctx *const ctx);  // 获得当前执行状态

4. 示例

/* 从状态 A 转换到状态 B */
smf_set_state(&ctx, &states[STATE_B]);

/* 不涉及层次状态机的执行顺序:
 * 1. states[STATE_A].exit() (如果存在)
 * 2. 更新 ctx->previous = &states[STATE_A]
 * 3. 更新 ctx->current = &states[STATE_B]
 * 4. states[STATE_B].entry() (如果存在)
 */

/* 典型的状态机执行模型 */
void state_machine_loop(void) {
    struct smf_ctx ctx;
    
    // 1. 初始化
    smf_set_initial(&ctx, &states[INITIAL_STATE]);
    
    // 2. 事件循环
    while (1) {
        // 处理输入或事件
        process_events();
        
        // 运行状态机
        int32_t ret = smf_run_state(&ctx);      // run函数中可能调用smf_set_state进行状态转换、也可能调用smf_set_terminate终止状态
        
        // 检查是否终止
        if (ret != 0) {                        // 返回0表示状态机正常可以继续转换,非0需要状态机终止
            break;
        }
        
        // 延时或其他操作
        delay_ms(10);
    }
}
posted @ 2025-12-27 21:57  gramming  阅读(19)  评论(0)    收藏  举报