【问题标题】:A call to delete [] crashes program, but not while debugging调用 delete [] 会导致程序崩溃,但在调试时不会
【发布时间】:2012-11-26 22:29:47
【问题描述】:

这是一个简单程序的代码,它应该读取每行包含一个单词的文本文件,动态分配存储所有单词所需的内存,将它们打印在屏幕上并释放使用的内存。

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

class Dict {
 public:
 int size;
 char ** words;
 Dict (int, int*);
 ~Dict ();
};

Dict::Dict(int s,int* sizes) {
 int i;
 size=s;
 words = new char* [s];
 for (i=0;i<s;i++)
  words[i] = new char [sizes[i]];
}

Dict::~Dict() {
 int i;
 for (i=0;i<size;i++) {
  delete [] words[i];
  printf("i=%d\n",i); // for debugging
  }
 delete [] words;
}

Dict loadDict (char* filename) {
 FILE* file;
 int n=0,i=0;
 int * sizes;
 char buff [64];

 file=fopen(filename,"r");
 while (!feof(file)) {
  n++;
  fscanf(file,"%*[^\n] \n");
 }
 sizes=new int [n];

 rewind(file);
 while (!feof(file)) {
  if (fscanf(file,"%s\n",buff)>0) {
   sizes[i]=strlen(buff);
   i++;
  }
 }

 rewind(file);
 Dict r(n,sizes);
 i=0;
 while (!feof(file)) {
  fscanf(file,"%s\n",r.words[i]);
  i++;
 }

 delete [] sizes;
 return r;
}

int main() {
 int i;
 Dict d=loadDict("dict.txt");
 for (i=0;i<d.size;i++)
 printf("%s|\n",d.words[i]);
 printf("%d DONE.\n",d.size);
 return 0;
}

释放是在 Dict 类的析构函数中完成的。但是,在仅包含几个单词的示例文本文件中使用,这些单词可以正确打印,但在执行delete [] words[i]; 形式的 3 行后,对 ~Dict 的调用会使应用程序崩溃。如果我使用 Code::Block 的调试器并在该行设置断点并告诉它在每个断点处继续,程序将正常终止。

由于这是一个非常简单的程序,我希望有一些简单的答案或修复!

【问题讨论】:

  • 这几乎不是 C++。如果您使用 C++,不妨利用标准库中包含的一些安全措施。
  • 尝试错误检查您的代码。
  • 看到答案如何提到 RoT,我提出这个:dl.dropbox.com/u/6101039/Modern%20C++.pdf

标签: c++ crash


【解决方案1】:

Dict 类具有动态分配的成员,但使用默认的复制构造函数和赋值运算符,这会导致在创建 Dict 的副本时,Dict 的两个实例指向同一个 words 数组,这种情况会发生当使用loadDict() 函数时。当Dict 实例之一被破坏时,它会为另一个Dict 实例留下一个悬空指针,并将导致words 数组及其元素的双重删除。

What is The Rule of Three?

如果这不是学习练习,请使用 std::vector&lt;std::string&gt; 和 C++ 流。例如:

std::vector<std::string> words;
...

std::ifstream in(filename);
std::string line;
while (in >> line) words.push_back(line);

【讨论】:

  • 谢谢,这也是一个有用的答案,我会尝试修正我糟糕的 C++ 编程方式。 :)
【解决方案2】:

你没有遵守三法则:

http://en.wikipedia.org/wiki/Rule_of_three_%28C%2B%2B_programming%29

Dict rloadDict 的末尾被破坏,而其自身的浅表副本返回到main。在main 的末尾,指针又是deleted。

就像在 C++ 中一样,这里不需要指针。在Dict 中存一个std::vector&lt;std::string&gt; 就没有什么花招了。

【讨论】:

    【解决方案3】:

    绝对错误的是您的代码没有复制构造函数并且您按值返回一个对象。这可能导致双重破坏,因此双重删除日期。这是否发生取决于是否省略了复制构造。

    此外,您为每个字符串分配了足够的字符来保存字符串,但不分配空终止符。也就是说,要存储长度为strlen(s) 的字符串s,您需要分配strlen(s) + 1 字符。

    我建议改用std::vector&lt;std::string&gt;:这样可以避免大部分问题。

    【讨论】:

    • 谢谢。邪恶的零角色显然是导致崩溃的原因。
    • 您返回 Dict 对象的方式很可能是复制构造函数被省略了,因此,我寻找了另一个问题。请注意,您还需要修复复制构造函数(和复制分配)!不能保证它们会被忽略。
    【解决方案4】:

    您的代码中存在一些问题:

    1. 您没有检查 fopen 是否返回有效指针。 (我得到这个是因为我尝试在没有 txt 文件的情况下运行代码)

    2. 您正在返回一个 Dict 对象,它将调用复制构造函数,而您没有定义一个。

    3. 您为 Dict 使用了一个赋值运算符,但您没有定义一个。

    您可以尝试将 loadDict() 更改为仅返回一个大小数组,然后将该数组传递给您的 Dict 构造函数。这样就不用写拷贝构造函数和赋值操作符了。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-03-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多