当前位置: 首页 > ops >正文

为什么打印出来的 cJSON type 值和头文件定义的不一样?

网罗开发(小红书、快手、视频号同名)

  大家好,我是 展菲,目前在上市企业从事人工智能项目研发管理工作,平时热衷于分享各种编程领域的软硬技能知识以及前沿技术,包括iOS、前端、Harmony OS、Java、Python等方向。在移动端开发、鸿蒙开发、物联网、嵌入式、云原生、开源等领域有深厚造诣。

图书作者:《ESP32-C3 物联网工程开发实战》
图书作者:《SwiftUI 入门,进阶与实战》
超级个体:COC上海社区主理人
特约讲师:大学讲师,谷歌亚马逊分享嘉宾
科技博主:华为HDE/HDG

我的博客内容涵盖广泛,主要分享技术教程、Bug解决方案、开发工具使用、前沿科技资讯、产品评测与使用体验。我特别关注云服务产品评测、AI 产品对比、开发板性能测试以及技术报告,同时也会提供产品优缺点分析、横向对比,并分享技术沙龙与行业大会的参会体验。我的目标是为读者提供有深度、有实用价值的技术洞察与分析。

展菲:您的前沿技术领航员
👋 大家好,我是展菲!
📱 全网搜索“展菲”,即可纵览我在各大平台的知识足迹。
📣 公众号“Swift社区”,每周定时推送干货满满的技术长文,从新兴框架的剖析到运维实战的复盘,助您技术进阶之路畅通无阻。
💬 微信端添加好友“fzhanfei”,与我直接交流,不管是项目瓶颈的求助,还是行业趋势的探讨,随时畅所欲言。
📅 最新动态:2025 年 3 月 17 日
快来加入技术社区,一起挖掘技术的无限潜能,携手迈向数字化新征程!


文章目录

    • 背景与问题描述
    • 原因(核心)—— 类型是用“位标志(bit flags)”来存储的
    • 推荐的使用方式(三种稳妥做法)
    • 可运行 Demo:演示类型索引与位掩码的关系,并给出映射函数
      • demo.c
      • 编译与运行(步骤)
      • 预期输出(示例)
    • 进一步的建议与实践要点
    • 总结

背景与问题描述

cJSON.h 里你可能看到类似这样的宏定义(这是为了描述“类型索引”):

#define cJSON_False     0
#define cJSON_True      1
#define cJSON_NULL      2
#define cJSON_Number    3
#define cJSON_String    4
#define cJSON_Array     5
#define cJSON_Object    6#define cJSON_IsReference 256

你运行程序时,打印 item->type 却看到这样的数值:

  • cJSON_String 打印成 16

  • cJSON_Number 打印成 8

  • 甚至还有 256 之类的值(IsReference

看起来“头文件里定义的数字”和运行时的 type 值“对不上”。所以你问:为什么会变成 816 等?是不是经过了左移(bit shift)操作?

原因(核心)—— 类型是用“位标志(bit flags)”来存储的

关键点:头文件里给出的那组数字(0、1、2、3、4……)是“类型索引/编号”,而不是 item->type 在运行时的存储值。

cJSON 在内部把类型表示成“位掩码(bit mask)/标志(flags)”。也就是说在运行时它把“类型编号”转换为 1 << typeIndex 这样一个位值来存储:

  • cJSON_False 索引 0 → 存储位值 1 << 0 = 1

  • cJSON_True 索引 1 → 存储位值 1 << 1 = 2

  • cJSON_NULL 索引 2 → 存储位值 1 << 2 = 4

  • cJSON_Number索引 3 → 存储位值 1 << 3 = 8

  • cJSON_String索引 4 → 存储位值 1 << 4 = 16

  • ...

还有一些额外的标志,比如 cJSON_IsReference 在头文件定义为 256(等于 1 << 8),这是为了把“引用”当作额外的位来表示(与类型位可以共存)。

所以,当你直接 printf("%d\n", item->type),你看到的是“位掩码”的值(如 816),而不是头文件那组“索引式”的数字(3、4)。

推荐的使用方式(三种稳妥做法)

  1. 优先使用 cJSON 提供的类型检查函数/宏(如果可用),例如 cJSON_IsString(item)cJSON_IsNumber(item),这样代码更清晰,不依赖内部实现细节(也更兼容未来 cJSON 的变动)。

  2. 如果要自己检查位值,使用位运算并加上移位(shift)逻辑,例如:

    if (item->type & (1 << cJSON_String)) { /* String */ }
    if (item->type & (1 << cJSON_Number)) { /* Number */ }
  3. 或者把 item->type 的位值转换成“可读名称”(写一个映射函数),这样打印出的结果会直观很多。

可运行 Demo:演示类型索引与位掩码的关系,并给出映射函数

下面是一个完整的 C Demo。说明:

  • 假设你把 cJSON.c / cJSON.h 放在同一目录下(或已安装 cJSON 库)。

  • 源文件名 demo.c

  • 编译方式在说明里给出(gcc 方式)。

注意:如果你没有 cJSON 源文件,可以从官方仓库克隆(https://github.com/DaveGamble/cJSON),把 cJSON.hcJSON.c 放到 demo 同目录即可。

demo.c

/** demo.c* 说明:*   - 演示 cJSON item->type 在运行时是位掩码(1 << typeIndex)*   - 提供一个把 typeFlag 转换为可读字符串的函数** 编译:gcc demo.c cJSON.c -o demo* 运行:./demo*/#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "cJSON.h"/* 用于把位掩码转换成人可读的类型名(可能有多个标志并存) */
const char* cJSON_GetTypeNames(int typeFlag) {static char buf[256];buf[0] = '\0';const char *names[] = {"False",   // index 0"True",    // index 1"Null",    // index 2"Number",  // index 3"String",  // index 4"Array",   // index 5"Object"   // index 6};int first = 1;for (int i = 0; i <= 6; ++i) {int mask = (1 << i); // runtime中的实际位掩码if (typeFlag & mask) {if (!first) strcat(buf, "|");strcat(buf, names[i]);first = 0;}}// 检查 cJSON_IsReference(如果存在)
#ifdef cJSON_IsReferenceif (typeFlag & cJSON_IsReference) {if (!first) strcat(buf, "|");strcat(buf, "IsReference");first = 0;}
#else/* 头文件通常定义 cJSON_IsReference 为 256 (1<<8) */if (typeFlag & (1 << 8)) {if (!first) strcat(buf, "|");strcat(buf, "IsReference");first = 0;}
#endifif (buf[0] == '\0') return "Unknown/None";return buf;
}/* 辅助:打印单个 item 的信息 */
void print_item_info(const cJSON *item) {if (!item) return;printf("key = \"%s\"\n", item->string ? item->string : "(root child)");printf("  raw item->type = %d\n", item->type);printf("  type bits (hex) = 0x%X\n", item->type);printf("  readable types = %s\n", cJSON_GetTypeNames(item->type));/* 演示如何使用位移检测(推荐) */if (item->type & (1 << cJSON_String)) {printf("  -> Detected via (item->type & (1<<cJSON_String)): STRING\n");}if (item->type & (1 << cJSON_Number)) {printf("  -> Detected via (item->type & (1<<cJSON_Number)): NUMBER\n");}if (item->type & (1 << cJSON_Array)) {printf("  -> Detected via (item->type & (1<<cJSON_Array)): ARRAY\n");}if (item->type & (1 << cJSON_Object)) {printf("  -> Detected via (item->type & (1<<cJSON_Object)): OBJECT\n");}/* 如果 cJSON 提供了检查宏,优先用宏(兼容性更好) */
#ifdef cJSON_IsStringif (cJSON_IsString((cJSON*)item)) {printf("  -> cJSON_IsString macro says: STRING\n");}
#endif
#ifdef cJSON_IsNumberif (cJSON_IsNumber((cJSON*)item)) {printf("  -> cJSON_IsNumber macro says: NUMBER\n");}
#endifprintf("\n");
}int main(void) {/* 测试 JSON */const char *json = "{""\"name\":\"Alice\",""\"age\":30,""\"active\":true,""\"items\": [1, 2, 3],""\"obj\": {\"k\":\"v\"},""\"nullval\": null""}";cJSON *root = cJSON_Parse(json);if (!root) {printf("parse error\n");return 1;}printf("Parsed JSON. Iterate top-level children:\n\n");for (cJSON *item = root->child; item != NULL; item = item->next) {print_item_info(item);}/* 额外演示:某些内部函数/字段 */printf("Note: cJSON header has defines like cJSON_String=%d, cJSON_Number=%d\n", cJSON_String, cJSON_Number);printf("But runtime item->type stores the bitmask (1<<typeIndex)\n");printf("So cJSON_String index=%d corresponds to runtime mask=1<<%d = %d\n\n", cJSON_String, cJSON_String, (1 << cJSON_String));cJSON_Delete(root);return 0;
}

编译与运行(步骤)

假设你已经把 demo.ccJSON.ccJSON.h 放在同一目录下:

gcc demo.c cJSON.c -o demo
./demo

预期输出(示例)

运行后你会看到类似这样的输出(注:数字可能随 cJSON 版本不同略有格式差异,但思路一致):

Parsed JSON. Iterate top-level children:key = "name"raw item->type = 16type bits (hex) = 0x10readable types = String-> Detected via (item->type & (1<<cJSON_String)): STRING-> cJSON_IsString macro says: STRINGkey = "age"raw item->type = 8type bits (hex) = 0x8readable types = Number-> Detected via (item->type & (1<<cJSON_Number)): NUMBER-> cJSON_IsNumber macro says: NUMBERkey = "active"raw item->type = 2type bits (hex) = 0x2readable types = True...key = "items"raw item->type = 32type bits (hex) = 0x20readable types = Arraykey = "obj"raw item->type = 64type bits (hex) = 0x40readable types = Objectkey = "nullval"raw item->type = 4type bits (hex) = 0x4readable types = NullNote: cJSON header has defines like cJSON_String=4, cJSON_Number=3
But runtime item->type stores the bitmask (1<<typeIndex)
So cJSON_String index=4 corresponds to runtime mask=1<<4 = 16

你可以看到 raw item->type168 等(即 1<<41<<3),而 cJSON_String 宏本身的数值仍是 4——两者不是同一个“语义”。这就是你遇到的“数值不一致”的根本原因。

进一步的建议与实践要点

  • 别直接比较 item->type == cJSON_String:这样会失败,因为 item->type 是位掩码(16),cJSON_String是索引(4)。

  • 优选使用库提供的宏/函数:如果 cJSON_IsString / cJSON_IsNumber 等宏存在,就直接用它们,语义最清晰,也最兼容不同 cJSON 版本。

  • 如果自己判断,记住左移:用 (item->type & (1 << cJSON_String)) 来检测。

  • 映射函数便于调试:调试时把 item->type 显示成可读的名字,能快速定位问题。

  • 注意复合标志:一个 item 可能同时带有 IsReference,因此一定用位检查而不是等于比较。

  • 如果你维护一个库,写封装函数:把类型判断封装成函数,避免到处重复写位运算。

总结

头文件里看到的 cJSON_String = 4 是“类型的索引”,而 item->type 在运行时是以“位掩码(1 << index)”的形式存储的;因此直接打印 item->type 会得到 1<<4 = 16 这样的数值。正确做法是使用 cJSON 提供的 cJSON_IsXxx() 宏或自己用 (item->type & (1 << cJSON_String)) 的位运算来判断类型,或者把位掩码转成可读字符串输出以便调试。

http://www.xdnf.cn/news/19921.html

相关文章:

  • git还原操作
  • ultralytics/nn/tasks.py源码学习笔记——核心函数parse_model
  • day2today3夏暮客的Python之路
  • 「逆向思维」的胜利:从“挤不上电梯”到“高效学习”的顶级心法
  • 2025年度GEO优化公司市场研究报告:技术驱动下的用户口碑洞察
  • Git的强软硬回退(三)
  • Docmost:面向现代团队的企业级Wiki
  • 鸿蒙:状态管理V2(V2装饰器的学习)
  • 超详细教程:一招一式教你将本地项目上传至GitHub
  • 【系统架构设计(13)】项目管理上:盈亏平衡分析与进度管理
  • SpringBoot 网络流量抓包与分析系统
  • 【RNN-LSTM-GRU】第一篇 序列建模基础:理解数据的“顺序”之力
  • Mac 使用 softhsm
  • 革新光纤锁模技术:《Light: Science Applications》报道纳米腔增强型可饱和吸收器
  • 质量管理里常见的缩写QA、QC、QE都是什么意思?
  • 彻底搞懂面向对象分析(OOA)
  • Linux内存管理章节一:深入浅出Linux内存管理:从物理内存到ARM32的用户与内核空间
  • 逻辑回归基础
  • .NET GcPDF V8.2 新版本:人工智能 PDF 处理
  • Spring Boot 根据配置优雅的决定实现类
  • Meshroom 2025.1.0安装及使用参数模板介绍:二维图片转三维重建
  • 因为对象装箱拆箱导致的空指针异常
  • C#强制类型转换(显示转换)和安全类型转换
  • 野火STM32Modbus主机读取寄存器/线圈失败(三)-尝试将存贮事件的地方改成数组(非必要解决方案)(附源码)
  • VBA中类的解读及应用第二十七讲:利用类完成查找的方案-5
  • SVT-AV1 svt_aom_motion_estimation_kernel 函数分析
  • 详细学习计划
  • 百度前端社招面经
  • 云手机运行是否消耗自身流量?
  • Docker(④Shell脚本)