【问题标题】:Using malloc instead of new causes free(): invalid pointer error使用 malloc 而不是 new 导致 free(): invalid pointer 错误
【发布时间】:2019-07-31 19:03:05
【问题描述】:

由于某种原因,使用 gcc 编译以下代码并在 Ubuntu 上运行它生成的二进制文件会出现 free(): invalid pointer 错误:

#include <stdlib.h>
#include <fstream>
#include <string>
#include <iostream>
#include <sstream>
#include <ios>
#include <new>

struct arr_double_size {
   double *array;
   int size;
};

struct Record {
   int ID;
   std::string str1;
   std::string str2;
   int num;
   struct arr_double_size values;
};

struct Record_array {
   struct Record *array;
   int size;
};

void Copy_Read(void) {
   std::ifstream file{"in_file"};
   std::ofstream new_file{"out_file"};
   std::string line;
   while (std::getline(file,line)) {
      new_file << line << std::endl;
   }
   file.close();
   new_file.close();
}

int main(void) {
   Copy_Read();
   struct Record rec;
   struct arr_double_size values;
   values.size = 1;
   values.array = (double *)malloc(1 * sizeof(double));
   values.array[0] = 72.12;
   rec.ID = 2718;
   rec.str1 = "Test1";
   rec.str2 = "Test2";
   rec.num = 1;
   rec.values = values;
   struct Record_array record_list;
   record_list.size = 1;
   record_list.array = (struct Record *)malloc(1 * sizeof(struct Record));
   record_list.array[0] = rec;   
   return 0;
}

in_file的内容是:

TEST TEST TEST

奇怪的是,将main 中的调用注释为Copy_Read 可以解决问题,将malloc 的调用替换为new 的调用也是如此。使用 gdb 运行程序显示尝试将rec 分配给record_list.array[0] 时出现错误。为什么会出现这种情况?我试图在这里举一个最小的例子;以前,此代码的扩展版本导致分段错误而不是 free(): invalid pointer 错误。我知道这是一个可怕的代码,永远不应该在严肃的程序中使用(我应该使用标准库 vectornew),但似乎有些东西我不明白并且没有很好的记录(无论如何,在初学者可以访问的资源中)关于 mallocnew 之间的区别,这是此代码问题的根源。

【问题讨论】:

  • 学习使用valgrind 并使用所有警告和调试信息编译您的代码(所以gcc -Wall -Wextra -g)。另请仔细阅读malloc的文档
  • 您不能使用malloc() 为包含非原始类型(也称为非 POD 类型)的结构/类分配内存。你的Record 结构包含std::string 类的字段,如果你使用malloc(),它们的构造函数将不会被正确调用。作为一般建议:不要在 C++ 中使用 malloc() / free()new / delete。请改用适当的容器类型或智能指针。
  • @BasileStarynkevitch 我确实使用您提到的所有选项进行了编译,并且 gcc 保持沉默。我计划进一步了解 valgrind 的使用,但还没有时间这样做。
  • 但不足之处在于你对C++的理解和知识。是的,C++ 是一种非常难的编程语言。
  • @flakmonkey - malloc 为用户分配一块内存——句号。它对您的代码一无所知,与您的代码的任何其他方面没有任何交互,并且对任何其他对象的默认构造一无所知。它只是分配一个内存块,成功时返回指向新块的指针,否则返回NULL。相比之下,C++ new 对应物确实知道正在构造的对象,并且会默认构造一个新对象。这就是为什么 C++ 有 new 而不是简单的 malloc

标签: c++ segmentation-fault malloc new-operator


【解决方案1】:

我不明白 malloc 和 new 的区别

您需要阅读更多关于 C++,例如一些C++ programming 的书,以及一个好的C++ reference 网站。是的,C++ 是一种非常困难的语言(你需要多年的工作才能掌握它)。稍后,您可以深入了解 C++11 标准 n3337(或一些更新的 C++ 标准)。您当然需要准确地了解constructorsdestructors 的作用(并解释这需要很多页,远远超出您在任何 StackOverflow 答案中的合理预期)。

您需要执行constructors 的代码(这可以通过new 完成,但不能通过malloc 完成) - 稍后也应该在释放内存之前执行析构函数。析构函数由delete(在许多其他情况下)调用,但当然不是free。另请阅读rule of fiveRAII

如果可能,您应该更喜欢使用smart pointers。有时(例如对于循环引用)它们是不够的。

害怕undefined behavior

valgrind 工具可用于查找与内存相关的错误。您还应该使用所有警告和调试信息进行编译,因此 g++ -Wall -Wextra -gGCC。您可能还想使用静态源代码分析器工具,例如 clang-analyzerFrama-C。使用它们可能需要很多专业知识;有no silver bullet

您的struct Record_array 错误:更喜欢使用std::vector&lt;Record&gt;。阅读有关标准 C++ 的更多信息containers

您的Record 的构造函数将调用str1str2 的构造函数(即std::string-s 的构造函数应用于两个不同的位置)。如果你不调用 Record 构造函数,str1str2 将保持某种未定义的状态(所以你一使用它们就会有一些 undefined behavior)。

malloc & free(对于 C)与 newdelete(对于 C++)之间的主要区别在于构造函数和析构函数的参与方式。当然,malloc & free 会忽略它们,但new & delete 不会。内存分配失败(例如,当虚拟内存耗尽时)也有不同的处理方式。

PS。在实践中,您不应该在 C++ 代码中使用 malloc,除非 - 仅在极少数情况下 - 在定义自己的 operator new 时。因为malloc 不调用C++ 构造函数(但new 调用)。另外,请理解C 和 C++ 是不同的编程语言malloc 用于 C,而不是 C++。许多C++ standard library 实现在他们的标准::operator new 实现中使用 malloc 并在他们的::operator delete 中使用free

【讨论】:

  • 这并不能解释为什么我会收到我收到的特定错误 (free(): invalid pointer)。这相当于说“您的问题是当您应该以 C++ 方式管理内存时,您正在尝试以 C 方式管理内存”。如果我简单地注释掉对Copy_Read 的调用,它也不能解释为什么代码实际上可以工作。这没有帮助。
  • 它确实解释了它,但间接地。我没有空间和时间来教你一本好的 C++ 编程书需要几章的内容。是的,C 和 C++ 是非常不同的语言。
  • @flakmonkey 这条语句record_list.array = (struct Record *)malloc(1 * sizeof(struct Record)); 无法调用Recordstd::string 的构造函数。然后这条语句record_list.array[0] = rec; 对未构造的字符串调用赋值运算符,导致错误。
  • @john 谢谢!请发表您的评论作为答案,以便我接受。
  • @flatmonkey:我的回答是一样的。我觉得你可以接受。
猜你喜欢
  • 2017-03-31
  • 2014-07-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-01
  • 1970-01-01
相关资源
最近更新 更多