news 2026/4/23 14:30:05

【GDB】调试Jsoncpp源码

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【GDB】调试Jsoncpp源码

前言:

起初在写jsoncpp样例的时候,写出了一个这样的悬垂指针的bug,代码如下:

int main() { Json::Value root; root["name"] = "zhangsan"; root["age"] = 18; root["sex"] = "mele"; root["score"].append(90); root["score"].append(80); root["score"].append(70); // 实例化 Json::StreamWriterBuilder builder; builder["indentation"] = ""; std::unique_ptr<Json::StreamWriter> writer(builder.newStreamWriter()); // 调用write函数 std::stringstream ss; int ret = writer->write(root, &ss); if(ret != 0) { cout << "write failed" << endl; } else { cout << "write success" << endl; cout << ss.str() << endl; } // // 反序列化 Json::CharReaderBuilder readerBuilder; std::unique_ptr<Json::CharReader> reader(readerBuilder.newCharReader()); Json::Value root2; std::string errors; bool ret1; ret1 = reader->parse(ss.str().c_str(), ss.str().c_str() + ss.str().size(), &root2, &errors); if(!ret1) { cout << "parse failed" << endl; } else { cout << "parse success" << endl; cout << root2["name"].asString() << endl; cout << root2["age"].asInt() << endl; cout << root2["sex"].asString() << endl; for(int i = 0; i < root2["score"].size(); i++) { cout << root2["score"][i].asInt() << endl; } } return 0; }

我发现ret1返回的是false而前面的代码又没问题,然后我想着GDB调试一下吧看看parse内部咋回事(当时我Ctrl 点击这个reader->parse发现进入的一直是头文件,那时的我以为GDB调试一下就能进入parse的源码)后面就衍生出了诸多问题。。。。。。。。

先说如何编译 然后直接GDB调试:

1.先把源码下载到本地

git clone https://github.com/open-source-parsers/jsoncpp.git

2.进入jsoncpp源码创建目录并开始编译

// 创建目录 mkdir build // 进入目录 cd build //因为源码是 set(CMAKE_BUILD_TYPE Release CACHE STRING 是release版的所以我们直接命令行输入DEBUG版的 cmake -DCMAKE_BUILD_TYPE=Debug .. // 编译 make

3.链接第三方库

这里我展示一下我的目录结构

// 这是我链接的方式 g++ -o main main.cc -I./jsoncpp/include -L./jsoncpp/build/lib -ljsoncpp -g -std=c++11 -Wl,-rpath=./jsoncpp/build/lib
// 查看一下链接信息 ldd main

OK已经连接第三方库了

gdb main
// 先,添加目录到源码搜索路径 (gdb) dir /home/dev/workspace/mystudy/jsoncpp/jsoncpp/src/lib_json // 打断点 (gdb) b 64 (gdb) b json_reader.cpp:1855 // 这里我们info b一下查看断点信息,这里Address<PENDING>不用管。后面我们就正常的调试就行了 (gdb) info b Num Type Disp Enb Address What 1 breakpoint keep y 0x0000000000004a12 in main() at main.cc:64 2 breakpoint keep y <PENDING> json_reader.cpp:1855

后面就使用正常的操作直接r 启动程序 n下一步 s进入函数内部这种的基础操作了。


最后得出的结论是调用ss.str()创建了两个不同的临时字符串对象,它们的指针指向不同的内存区域,导致parse函数收到了无效的指针范围。

正确写法

string str = ss.str(); ret1 = reader->parse(str.c_str(), str.c_str() + str.size(), &root2, &errors);

可是我打印的地址都一样啊。

std::cout << (void*)ss.str().c_str() << std::endl; // 0x1234 std::cout << (void*)ss.str().c_str() << std::endl; // 0x1234

但这只是巧合或编译器优化,不能依赖!原因:1.第一个临时对象销毁后,其内存可能被立即重用。2.编译器可能进行优化,但不能保证。

> - < 哭了,原来编译器优化也增加学习成本。

举个栗子

#include <cstring> #include <iostream> #include <sstream> void test_pointers(const char *start, const char *end) { std::cout << "start: " << (void *)start << ", end: " << (void *)end << std::endl; // 检查是否在同一个内存块 size_t block_size = (end > start) ? (end - start) : 0; std::cout << "Block size calculated: " << block_size << std::endl; // 尝试读取(可能崩溃或读取错误数据) if (start && end && end > start) { std::cout << "First few chars at start: "; for (int i = 0; (start + i) < end; i++) { std::cout << start[i]; } std::cout << std::endl; } } int main() { std::stringstream ss; ss << R"({"name":"zhangsan"})"; // 错误方式 std::cout << "=== 错误方式(临时对象)===" << std::endl; const char *start_bad = ss.str().c_str(); const char *end_bad = ss.str().c_str() + ss.str().size(); test_pointers(start_bad, end_bad); std::cout << "\n=== 正确方式(单个对象)===" << std::endl; // 正确方式 std::string jsonStr = ss.str(); const char *start_good = jsonStr.c_str(); const char *end_good = start_good + jsonStr.size(); test_pointers(start_good, end_good); return 0; }

你学废了吗?

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/23 14:27:24

学生认证通道开启:免费获取LobeChat高级功能

学生认证通道开启&#xff1a;免费获取LobeChat高级功能 在AI技术加速渗透教育、科研与日常生活的今天&#xff0c;越来越多的学生开始尝试构建自己的智能助手——不是为了炫技&#xff0c;而是真正用它来写论文、读文献、学编程、做项目。但问题也随之而来&#xff1a;主流AI平…

作者头像 李华
网站建设 2026/4/22 18:58:03

LobeChat在线测评自动评分系统

LobeChat在线测评自动评分系统 在教育数字化转型加速的今天&#xff0c;高校与在线教育平台正面临一个共同难题&#xff1a;如何高效、公平地评估成千上万学生的开放式问答或论述题作答&#xff1f;传统人工批改耗时费力&#xff0c;而简单的关键词匹配又难以捕捉语义深度。随着…

作者头像 李华
网站建设 2026/4/13 3:25:15

基于单片机的无刷直流电机调速控制设计

2系统设计方案 2.1总体设计 本文基于stm32实现一种无刷直流电机调速系统&#xff0c;主要实现对无刷直流电机的精确调速控制、工作状态显示、便于操作的人机界面等。具体如下&#xff1a; 电机驱动与调速&#xff1a;通过MCU输出6路PWM信号&#xff0c;控制6路功率MOS管组成的驱…

作者头像 李华
网站建设 2026/4/21 19:26:33

LobeChat移动端访问体验优化方案

LobeChat移动端访问体验优化方案 在移动设备占据用户上网时长超过70%的今天&#xff0c;一个AI聊天应用能否在手机上“好用”&#xff0c;几乎直接决定了它的实际价值。尽管许多大模型前端界面设计精美、功能丰富&#xff0c;但一旦进入手机浏览器&#xff0c;往往暴露出生硬的…

作者头像 李华
网站建设 2026/4/17 4:16:45

Leetcode刷题日记14(131-140)

目录问题1&#xff1a;问题链接&#xff1a;问题描述&#xff1a;实例&#xff1a;代码&#xff1a;问题2&#xff1a;问题链接&#xff1a;问题描述&#xff1a;实例&#xff1a;代码&#xff1a;问题3&#xff1a;问题链接&#xff1a;问题描述&#xff1a;实例&#xff1a;问…

作者头像 李华
网站建设 2026/4/16 2:15:03

10章 数据共享操作 - “Vega“ 7nm Instruction Set ArchitectureReference Guide

本地数据共享&#xff08;LDS&#xff09;是一种极低延迟、用于临时数据的RAM暂存器&#xff0c;其有效带宽至少比直接、无缓存的全局内存高出一个数量级。它允许工作组内的工作项之间共享数据&#xff0c;并用于保存像素着色器参数插值所需的参数。与只读缓存不同&#xff0c;…

作者头像 李华