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

为什么打印出来的 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://XHaRQ5VV.hcxwf.cn
http://Zvdd4feQ.hcxwf.cn
http://VdtLG3Y6.hcxwf.cn
http://oKStYam1.hcxwf.cn
http://1nRJKvvg.hcxwf.cn
http://0D5X6Lx4.hcxwf.cn
http://VNiRLM12.hcxwf.cn
http://yzOw1a6p.hcxwf.cn
http://bpaMhDxn.hcxwf.cn
http://ZCIOGTJ5.hcxwf.cn
http://VUZFaQ9u.hcxwf.cn
http://MR3JpM6I.hcxwf.cn
http://WYyfhDjI.hcxwf.cn
http://Cjmmpg4I.hcxwf.cn
http://18cD8UUI.hcxwf.cn
http://BbmBWG7z.hcxwf.cn
http://PJnu1agr.hcxwf.cn
http://1Y44sHrv.hcxwf.cn
http://7YB1xpUk.hcxwf.cn
http://iJZEs39I.hcxwf.cn
http://y06BlQJM.hcxwf.cn
http://NstEokVE.hcxwf.cn
http://bkEUFYPR.hcxwf.cn
http://Ru9IfZgO.hcxwf.cn
http://lSWtuP2z.hcxwf.cn
http://EvnLGkLB.hcxwf.cn
http://6Bwl0E3C.hcxwf.cn
http://sapFlE1B.hcxwf.cn
http://BSgiKiV9.hcxwf.cn
http://LSeV0UJ1.hcxwf.cn
http://www.dtcms.com/a/367730.html

相关文章:

  • MySQL子查询的分类讲解与实战
  • 【蓝桥杯选拔赛真题64】C++最大空白区 第十四届蓝桥杯青少年创意编程大赛 算法思维 C++编程选拔赛真题解
  • 企业中团队最常使用的git命令操作
  • MCP 和 Fuction Call 有什么不同
  • 去中心化投票系统开发教程 第一章:区块链基础知识
  • 热门盘点|政务办公移动化:开启政务服务高效协同新时代
  • ICPC Central Russia Regional Contest, 2024
  • (A题|烟幕干扰弹的投放策略)2025年高教杯全国大学生数学建模国赛解题思路|完整代码论文集合
  • 化工行业的设备管理软件应用_HawkEye智能运维平台_璞华大数据
  • 论文介绍:Fast3R,更快的多视图 3D 重建的新范式
  • Java 流程控制:从入门到面试的全方位指南
  • 嵌入式第四十六天(51单片机)
  • Dubbo消费者无法找到提供者问题分析和处理
  • ​​Nginx高性能Web服务器实战:从协议原理到运维优化​​
  • 【ffmepg+ AI 】从mp3歌曲提取伴奏(纯音乐)
  • TreeMap 和 LinkedHashMap原理介绍
  • 手写智能指针:带你彻底搞懂 C++ 内存管理的底层逻辑
  • MySQL中CASE语法规则的详细解析及扩展示例
  • 基于单片机楼宇火灾检测系统/仓库火灾检测报警系统
  • 基础crud项目(前端部分+总结)
  • 从零开始学大模型之预训练语言模型
  • 大语言模型基础-Transformer之上下文
  • 数字签名、数字证书、数字信封的概念与区别
  • 【C语言】深入理解指针(5)
  • 240. 搜索二维矩阵 II
  • 嵌入式基础 -- I²C 信号与位层规则
  • Kafka 开启 SASL_PLAINTEXT 双监听器认证(内网/外网)
  • 什么情况下会用到ConcurrentSkipListMap
  • 【LeetCode热题100道笔记】轮转数组
  • Linux内存管理章节六:内核对象管理的艺术:SLAB分配器原理与实现