【问题标题】:C++ - Delete std::string*; heap corruptionC++ - 删除 std::string*;堆损坏
【发布时间】:2012-08-12 19:47:36
【问题描述】:

我对 C++ 内存管理比较陌生,我遇到了这个奇怪的堆损坏错误(加上之前 Visual Studio 中的自动断点)。这是有问题的代码:

z_world::z_world(char* name)
{
    unsigned int i, skip;
    char tmp;

    //Load data from file
    std::string* data = loadString(name);

    //Base case if there is no world data
    tiles = NULL;

    w = 0;
    h = 0;

    if(data->length() > 0) {
        //Set up the 'tiles' array
        for(i = 0; i < data->length(); i++) {
            if(data->at(i) == '\n')
                h++;
            if(h == 0)
                w++;
        }
        tiles = new int[data->length()-h];

        //Load Data
        skip = 0;
        for(i = 0; i < data->length(); i++) {
            if(data->at(i) == '\n') {
                skip++;
                printf("\n");
                continue;
            }
            tmp = data->at(i);
            tiles[i+skip] = atoi(&tmp);
            printf("%i ",tiles[i+skip]);
        }
    }
    delete data;
}

这里是我加载字符串的地方:

std::string* loadString(char* name)
{
    ifstream in(name);
    std::string* input = new string();

    while(in) {
        std::string line;
        getline(in,line);
        input->append(line);
        input->append("\n");
    }

    in.close();

    return input;
}

我在“删除数据”中得到了断点和错误,这让我认为“数据”在此之前的某个地方被删除了,但我找不到它会在哪里。作为参考,此方法是创建一个对象,该对象以虚拟 2D 整数数组的形式包含游戏的世界数据(用于图块的 ID)。

【问题讨论】:

  • 你最好只按值返回一个字符串而忘记内存管理。
  • 你确定是tiles[i+skip] 而不是tiles[i-skip]
  • 数据不会在其他任何地方被删除 - 但它可能会损坏,因为例如您正在写入超出切片数组的范围。 2 个修复:1) 不使用原始指针,但使用智能指针或按值传递 std::string 2) 使用 std::vector 而不是原始数组

标签: c++ string memory-management


【解决方案1】:

你的问题可能在这里:

tiles[i+skip] = atoi(&tmp);

问题1:
应该是-skip

tiles[i - skip] =

问题 2:
atoi() 命令使用不正确(tmp 不包含字符串)。但我也不认为atoi() 是合适的方法。我认为您正在寻找的是简单的任务。从 char 到 int 的转换是自动的:

 tiles[i - skip] = tmp;

问题 3:
您没有正确使用对象。在这种情况下,没有必要生成动态对象和动态内存管理造成混乱。只创建自动对象并正常传递它们会更简单:

std::string* loadString(char* name)
      //   ^  Don't do this.



std::string loadString(std::string const& name)
//  ^^^^^^^  return a string by value.
//           The compiler will handle memory management very well.

一般来说,你不应该传递指针。在您确实需要指针的少数情况下,它们应该保存在智能指针对象或容器(用于多个对象)中,以便正确控制它们的寿命。

【讨论】:

  • 这行得通,谢谢!堆损坏错误是由问题1引起的,但我也根据您和其他人的建议修复了其他问题。
  • 您还应该查看:tiles = new int[data-&gt;length()-h]; 另一个不需要的 new 用法。更喜欢使用std::vector&lt;int&gt; 作为子对象的容器。
【解决方案2】:

atoi(&amp;tmp); atoi 需要一个指向空终止字符串的指针 - 而不是指向 char 的指针

【讨论】:

    【解决方案3】:

    无需在您显示的代码中动态分配字符串。将loadString 函数更改为

    std::string loadString(char* name)
    {
        ifstream in(name);
        std::string input;
    
        // ...
    
        return input;
    }
    

    在调用者中

    std::string data = loadString( name );
    

    现在你完成后不需要delete 字符串了。

    代替

    int *tiles = NULL;
    tiles = new int[data->length()-h];
    

    使用

    std::vector<int> tiles;
    tiles.resize(data.length() - h);
    

    此外,如果您确实需要动态分配对象,您应该使用智能指针(std::unique_ptrstd::shared_ptr)而不是原始指针。

    【讨论】:

    • 我不确定这是否真的是问题所在。他用 new 分配,然后删除一次,无论问题是什么,它也可能会在堆栈上表现出来。
    • @enobayram 这不是他看到的堆损坏的原因,这可能与@stijn 在上面的评论中所述覆盖tiles 数组的边界有关。
    【解决方案4】:

    有一个错误

    tiles[i+skip] = atoi(&tmp);
    

    例如,对于一个字符串

    Hello\n
    World\n
    

    对于i == 10点的循环迭代,skip已经是1(因为我们之前遇到过第一个\n)并且您正在写信给tiles[10 + 1],但tiles只有分配为具有 10 个元素的数组。

    【讨论】:

      【解决方案5】:

      可能是该函数的变量输入是本地的。因此,从这里返回后,内存被释放。因此,稍后在此字符串上调用 delete 会尝试释放已释放的内存。

      【讨论】:

        猜你喜欢
        • 2013-06-04
        • 1970-01-01
        • 1970-01-01
        • 2013-04-20
        • 1970-01-01
        • 2019-08-28
        • 2013-01-23
        • 2017-01-18
        • 2011-05-12
        相关资源
        最近更新 更多