【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;
}

你学废了吗?

相关推荐
H.莓飛1 分钟前
【Linux】自主 Shell
linux·运维·服务器·c语言·开发语言·visual studio
roman_日积跬步-终至千里1 分钟前
【数据Agent论文·分析与报告评测|27】ComInsight:原子洞察正确,组合后的结论就可靠吗?
人工智能·算法·机器学习·数据分析
熊猫钓鱼>_>4 分钟前
Kotlin Multiplatform for OpenHarmony 实战:kotlinx-coroutines 结构化并发适配
开发语言·kotlin·agent·harmonyos·openharmony·适配·华为云码道
沫璃染墨17 分钟前
《Qt从零入门系列(十四):QThread多线程详解——从线程创建到客户端阻塞处理》
开发语言·c++·笔记·qt·ui·交互·线程
草莓熊Lotso18 分钟前
【C++ 基于微服务的即时通讯系统】Elasticsearch 从安装到 C++ 客户端二次封装全落地
linux·网络·数据库·c++·elasticsearch·微服务
Samson Bruce19 分钟前
【数学之美】
线性代数·算法·概率论
hixiong12330 分钟前
C# 使用 ZXingCpp.Angle 进行条码、二维码识别
开发语言·c#·扫码
CHPCWWHSU30 分钟前
FreeCAD 源码解读:从 Property 到 Command,看一套 CAD 内核的完整闭环
c++·cad
云和数据.ChenGuang31 分钟前
AI 工程师相关就业岗位(细分,偏工程落地,由底层到上层)
服务器·开发语言·人工智能·python
爱读书的小胖41 分钟前
Codewars:Last digit of a large number(C)解题分析
c语言·开发语言·r语言