【问题标题】:Unexpected behavior when converting a character array to a string将字符数组转换为字符串时的意外行为
【发布时间】:2021-09-07 04:12:44
【问题描述】:

所以我有一个简单的类,它接受一个字符数组并将其解析为一个 JSON 对象。然后它在内部存储该对象并提供一个 getter。

class JSONContainer {
public:
    explicit JSONContainer(const char* const json) {
        std::string t(json);

        _json = new nlohmann::basic_json(json);
    }

    ~JSONContainer() {
        delete _json;
    }

    nlohmann::json *j() {
        return _json;
    }

private:
    nlohmann::json* _json;
};

如果我用一些简单的东西来实例化这个类......

{"data": [100,100]}

它可以工作,但是如果这个字符串增长到 ~1000+ 的长度,当我尝试将 json 解析为一个字符串时,传入的字符数组就会损坏。

                      // incoming json {"data": [100,100,100,100,100...
std::string t(json);  // turns into "ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ..." after this line

我不知道是什么原因造成的。我想检查的一件事是 json 末尾是否存在空终止符,我总是找到它。

感谢您的帮助!

cmets 的附加上下文 ...

这是调用上面构造函数的方法……

std::shared_ptr<void> JSONSerDes::deserialize(const char *serializedData) {
    auto *ct = new JSONContainer(serializedData);
    return std::shared_ptr<void>(ct);
}

然后上栈到主函数,注意这一行deserializedData = t-&gt;deserialize(serializedData); ...

...
    // declare intermediate data
    const char* serializedData;
    std::shared_ptr<void> deserializedData;

    // for each data set size, run each test
    for (const int testSize: sizeTestsB) {
        // generate the test data, imitate data coming from python program
        PyObject* td = data(testSize);

        for (const std::unique_ptr<SerDesTest>& t: tests) {
            // log the start
            startTest(t->type(), testSize, currentTest, totalTests);

            // mark start, ser/des mark end
            start = std::chrono::steady_clock::now();

            serializedData = t->serialize(td);                                      // Python -> Redis
            checkpoints.push_back(checkpoint(t->type(), testSize,  "PythonToRedis", start));

            deserializedData = t->deserialize(serializedData);            // Redis -> Container
            checkpoints.push_back(checkpoint(t->type(), testSize,  "RedisToContainer", start));
...

这是用于将python对象转换为字符数组的函数。 dumps 是来自 pythons json 模块的一种方法。我可能误解了字符数组的生命周期是什么。

const char* JSONSerDes::serialize(PyObject * pyJson) {
    // convert pyobject to boost python object
    boost::python::object d = boost::python::extract<boost::python::object>(pyJson);

    // call the dumps function and capture the return value
    return boost::python::extract<const char*>(dumps(d));
}

【问题讨论】:

  • 能否展示构造代码,包括构造函数参数的生命周期相关代码?
  • @SR_ 更改帖子以提供更多上下文。
  • t-&gt;serialize(td) 返回什么?我的猜测是它返回一个指向已释放std::string 的指针,因此该值对于短字符串(小字符串优化)设法保持活动状态,但对于长字符串(溢出到堆上)失败。字符Í是十六进制0xCD,是常用的调试填充值。根据this table,它被MSVC用于未初始化的堆数据。 (还要注意shared_ptr&lt;void&gt; 将是内存泄漏,因为它不知道如何破坏JSONContainer。)
  • @RaymondChen 它返回一个字符数组,表示可以写入文件/写入redis的数据缓冲区。我也在我的帖子中包含了该方法。我不知道如何在堆上创建一个 json 对象,所以这就是我创建该容器对象的原因。如您所见,我仍在学习,因此非常感谢您提出任何建议
  • 我更新了我的帖子以包含一个具有析构函数的新容器,行为相同

标签: c++ stdstring nlohmann-json c-str


【解决方案1】:

我知道出了什么问题。当这个句柄

boost::python::object rv = dumps(d);

超出范围,数据似乎被删除了,这让我觉得“提取的”指针只是引用内部数据,而不是作为提供的类型复制出来的数据。

我刚刚更改了我的序列化方法,将数据复制到我在堆上分配的新缓冲区。

const char* JSONSerDes::serialize(PyObject * pyJson) {
    // convert pyobject to boost python object
    boost::python::object d = boost::python::extract<boost::python::object>(pyJson);

    // capture the return value of the return value
    boost::python::object rv = dumps(d);
    const char* prv = boost::python::extract<const char*>(rv);
    size_t prvLen = strlen(prv);

    // copy and return
    char* rvBuffer = new char[prvLen + 1];
    rvBuffer[prvLen] = '\0';
    strncpy(rvBuffer, prv, strlen(prv));

    // call the dumps function and capture the return value
    return rvBuffer;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-08-03
    • 2014-10-25
    • 2017-10-15
    • 2011-06-18
    • 2011-02-15
    相关资源
    最近更新 更多