【问题标题】:How did this memory corruption occur in c++?这种内存损坏是如何在 C++ 中发生的?
【发布时间】:2014-07-22 16:19:47
【问题描述】:

我不确定我是否理解下面的错误是什么

const char* packs[] = {"zero","one","two","three","four",..."twelve"} //abbreviated for post

struct packinfo {
    char* data;
    int   len;
};

std::vector<packinfo> k;
k.reserve(10000);

for (int i = 0; i < 10; ++i) {
    const char* data = packs[i];
    packinfo tobuf;
    tobuf.data = new char[strlen(data)];
    tobuf.len = strlen(data);
    memcpy(tobuf.data, data, strlen(data));
    k.push_back(tobuf);
}

for (int i = 0; i < k.size(); ++i)
    std::cout << "k[" << i << "]: " << k[i].data << ", ";
std::cout << std::endl;

for (int i = 0; i < k.size(); ++i) {
    packinfo& pack = k[i];
    bool foo = (i < 5);
    if (foo) std::cout << "inspecting k[" << i << "]: " << k[i].data << std::endl;
    delete pack.data;
    if (!foo) {
        k.erase(k.begin(), k.begin() + i);
        packinfo tobuf;
        const char* data = packs[10];
        tobuf.data = new char[strlen(data)];
        tobuf.len = strlen(data);
        memcpy(tobuf.data, data, strlen(data));
        break; //intentionally forgot to push_back
    }
}

for (int i = 0; i < k.size(); ++i)
    std::cout << "k[" << i << "]: " << k[i].data << ", ";
std::cout << std::endl

;

上面运行的输出如下:

k[0]: zero, k[1]: one, ... , k[9]: nine, //all as expected
inspecting k[0]: zero
inspecting k[1]: one
...
inspecting k[4]: four
k[0]: ten^], k[1]: six, k[2] seven, k[3]: eight, k[4]L nine, //gargabe crept in

垃圾是如何爬到向量的开头的?

【问题讨论】:

  • 阅读How to create a Minimal, Complete, and Verifiable example 对调试问题很有帮助。
  • 听说过std::string吗?立即摆脱 char* 的东西,您的所有问题很可能都会得到解决。
  • 创建自己的字符串类型是一种学习练习吗?永远记住你是否有一个以 0 结尾的字符串或一个计数字符串(可能同时是两个),并且不要忘记终止符分别不要使用 cstring-functions。
  • 整个字符串部分无关紧要,我实际上只是想找出一个更大的问题,其中 std::string 或 strlen 不存在,但我试图将逻辑归结为仍然存在的东西分配和 memcopies 看看我是否能找出问题..strlen 固定为 (strlen + 1) 仍然将 10 放在向量的头部,我不明白这是怎么发生的
  • 由于您使用new[]packinfo.data 分配内存,因此您必须使用delete[] 释放内存(注意方括号)。嘿,您知道std::string 可以让您免于所有这些容易出错的手动资源管理吗?

标签: c++ memory-leaks stdvector


【解决方案1】:

strlen 为您提供 nul 终止字符串中的字符长度不计算 nul 终止字符。因此,您正在动态分配一个太短而无法容纳目标字符串的数据缓冲区:

tobuf.data = new char[strlen(data)]; // too short by 1

当你使用 memcpy 填充它时,字符串没有空终止符的空间,如果有的话你也不会复制它,因为数组太短了:

memcpy(tobuf.data, data, strlen(data)); // tobuf.data is not nul-terminated

当您尝试读取它,就好像它是一个以 nul 终止的字符串一样,您超出了界限。

直接的解决方法是使用strlen(data) +1,但您真正应该做的是通过将packinfo 替换为std::string 来避免整个问题。

std::vector<std::string> k;
k.reserve(10000);

【讨论】:

  • 好吧 strlen(data)+1 摆脱了垃圾......但是当我在最后转储元素时,为什么向量的头部是 10?
  • @PalaceChan 不知道。您在其他地方还有其他错误。
  • +1 表示“通过用 std::string 替换 packinfo 来避免整个问题”。
【解决方案2】:

问题在于以下几行:

tobuf.data = new char[strlen(data)];
tobuf.len = strlen(data);
memcpy(tobuf.data, data, strlen(data));

在哪里为字符串终止符添加空格?

C++ 有 std::string 类,你应该真正使用它,因为它可以帮助你解决这些问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-06-04
    • 1970-01-01
    • 1970-01-01
    • 2021-10-07
    • 2012-01-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多