Valgrind:"Invalid write of size 1" 在 char[] 到 std::vector<char> 迁移之后

Valgrind : "Invalid write of size 1" after char[] to std::vector<char> migration

我在 C++ 中有一个非常简单的 hexlify 方法,它基于 python 的 binascii 实现。

std::string Hexlify(const std::string& iData)
{
    // the same as python binascii.b2a_hex
    const size_t len = iData.size() << 1; // output will be twice as long
    char hex[len];

    const char* curdata = iData.data();
    char* curhex = hex;
    const char* end = curdata + iData.size();

    char c;
    // from python's implementation (2.7.1, if it matters)
    while(curdata <= end)
    {
        c = (*curdata>>4) & 0xf;
        c = (c>9) ? c+'a'-10 : c + '0';
        *(curhex++) = c;
        c = (*curdata) & 0xf;
        c = (c>9) ? c+'a'-10 : c + '0';
        *(curhex++) = c;
        curdata++;
    }
    return std::string(hex, len);
}

这很好用。

现在,hex char[] 被分配到堆栈上,在处理巨大的缓冲区时可能会出现问题,这就是为什么我想将它迁移到 std::vector 以从中受益堆分配。

std::string Hexlify(const std::string& iData)
{
    // the same as python binascii.b2a_hex
    const size_t len = iData.size() << 1; // output will be twice as long
    std::vector<char> hex(len);

    const char* curdata = iData.data();
    char* curhex = &hex[0];
    const char* end = curdata + iData.size();

    // SAME CODE AS BEFORE

    return std::string(&hex[0], len);
}

std::vector 实现生成 Valgrind 的 "Invalid write of size 1" 错误。

知道为什么吗?

如果我让hex向量大两个字节(一个似乎不够)

std::vector<char> hex(len + 2);

错误从 valgrind 的报告中消失。

因为你落后一分。

如果iData表示"xy",那么end指向"y"之后的一个。使用您的 <= end,您尝试将 3 个字符(给出 6 个十六进制数字)编码为 space 只够 4.

您的问题是 end 的 off-by-one 错误。您可以通过使用 C++ 中的一些 higher-level 概念来避免这种棘手的边缘情况,而不会降低运行时速度。特别是,range-based forstd::string 作为标准容器的使用意味着我们可以在没有边界测试或指针算法的情况下实现这一点:

#include <string>

std::string hexlify(const std::string& s)
{
    static auto const to_hex = [](char c)
        {
            c &= 0xf; return c>9 ? c+'a'-10 : c+'0';
        };

    std::string result;
    result.reserve(2 * s.size());

    for (auto b: s) {
        result.push_back(to_hex(b>>4));
        result.push_back(to_hex(b));
    }
    return result;
}


int main()
{
    static auto const test = [](const std::string& in,
                                const std::string& expected)
        {
            return hexlify(in) != expected;
        };

    // We use numeric character escapes to avoid dependence on any
    // specific character coding.
    return test("", "30")
        +  test({"[=10=][=10=]", 1}, "00")
        +  test({"[=10=][=10=]", 2}, "0000")
        +  test("7", "ff");
}

那是 Valgrind-clean。