【问题标题】:Segmentation fault in new operator新运算符中的分段错误
【发布时间】:2020-06-17 16:42:00
【问题描述】:

更新:修正malloc(sizeof(500))后仍然存在分段错误

我在以下代码中遇到分段错误:

#include <iostream>
#include <vector>
#include <functional>

struct Data {
  std::string name;

  std::function<int()> work;

  std::vector<int*> v1 {nullptr, nullptr, nullptr, nullptr};
  std::vector<int*> v2 {nullptr, nullptr, nullptr, nullptr};

  int* v3 {nullptr};
  int* v4 {nullptr};
};

int main() {
  std::cout << sizeof(Data) << "\n";   // Just to make sure sizeof(Data) < 500

  auto raw = malloc(500);      // Allocate memory

  auto int_ptr = static_cast<int*>(raw);
  int_ptr ++; 

  auto ptr = reinterpret_cast<Data*>(int_ptr);
  new (ptr) Data();                     // GDB reports segmentation fault here

  free(raw);
}

编译命令:

clang++-9 -std=c++14 -stdlib=libc++ -ggdb3 prog.cpp -lc++abi -O2

我认为内存应该足够大,可以存储Data,但是为什么new会出现分段错误?

【问题讨论】:

  • 可能的对齐问题?
  • godbolt.org/z/m36kaz: 错误:malloc.c:2401: sysmalloc: 断言`(old_top == initial_top (av) && old_size == 0) || ((unsigned long) (old_size) >= MINSIZE && prev_inuse (old_top) && ((unsigned long) old_end & (pagesize - 1)) == 0)' 失败。
  • 在我纠正错字sizeof(500) 后,编译器探索没有断言错误。但是分段错误仍然存​​在。
  • 你有分段错误的回溯(bt in gdb)吗?

标签: c++ pointers segmentation-fault malloc reinterpret-cast


【解决方案1】:

你的问题在这里:

auto raw = malloc(sizeof(500)); 

'500' 是一个整数,所以sizeof(500) 返回 4。我认为你的意思是:

auto raw = malloc(500); 

无关,但您可能想这样做:

  auto int_ptr = static_cast<int*>(raw);
  int_ptr += 2; 

只是让指针保持 8 字节对齐(假设您使用的是 64 位操作系统)。 另外,不要忘记在调用 free 之前调用 dtor(否则你有内存泄漏)

ptr->~Data();

【讨论】:

  • 感谢您指出这一点! sizeof 是我复制代码时的拼写错误。将其更改为 malloc(500) 后,我仍然遇到相同的分段错误。
【解决方案2】:

如果您正在编写 C++,通常不适合使用 malloc。您可以使用向量作为缓冲区,或创建一个字符数组。

int main() {
  std::vector<char> buffer(500);

  auto ptr = reinterpret_cast<Data*>(buffer.data());
  new (ptr) Data;   
}

【讨论】:

  • 请注意,如果您按照建议在堆栈上使用向量,则缓冲区在退出范围时会失效。可能这不是某人需要替换malloc 电话的原因。在退出范围之前还需要 ptr-&gt;~Data(),对于使用放置 new 创建的对象,编译不会调用 dtor
【解决方案3】:

通常,由于对齐不正确,您的代码会有未定义的行为。我将尝试在下面的示例中进行解释。

假设您使用的是 64 位架构,具有 4 字节 int。在这样的系统上,malloc 通常会为您提供 16 字节对齐的内存块。然后,当您向该边界添加 4 个字节(raw 加上 4 个字节)时,生成的地址 (ptr) 是 4 字节对齐的,但既不是 8 字节也不是 16 字节对齐。

现在,Data 有一些自然对齐,很可能是 8 字节。您可以通过alignof(Data) 表达式找到它。当您尝试在 4 字节对齐的地址上创建 Data 类型的对象时,此 results in undefined behavior


另外,正如其他人所指出的,您需要为 placement new 创建的对象手动调用析构函数。

【讨论】:

    猜你喜欢
    • 2020-07-14
    • 1970-01-01
    • 1970-01-01
    • 2013-11-21
    • 1970-01-01
    • 2014-11-30
    • 2016-01-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多