使用非聚合成员转换结构

1 c++ segmentation-fault

当我尝试重新解释包含向量的结构时,我收到分段错误 (SIGSEGV)。以下代码本身没有意义,但显示了一个最小的工作(失败)示例。

// compiler: g++ -std=c++17
struct Table
{
     std::vector<int> ids;
};

std::vector<std::byte> storage;

// put that table into the storage
Table table = {.ids = {3, 5}};
auto convert = [](Table x){ return reinterpret_cast<std::byte*>(&x); };
std::byte* bytes = convert(table);
storage.insert(storage.end(), bytes, bytes + sizeof(Table));

// ...

// get that table back from the storage
Table& tableau = *reinterpret_cast<Table*>(&storage.front());
assert(tableau.ids[0] == 3);
assert(tableau.ids[1] == 5);
Run Code Online (Sandbox Code Playgroud)

如果我内联转换函数,代码可以正常工作,所以我的猜测是一些底层内存被删除。Convert 函数创建表的本地副本,并在离开该函数后,调用本地副本的 ids 向量的析构函数。重铸仅返回向量,但 id 已被删除。

这是我的问题:

  1. 为什么会出现分段错误?(我的猜测正确吗?)
  2. 我该如何解决这个问题?

先谢谢了

Sam*_*hik 5

我在所显示的代码中看到至少三个未定义行为的原因,这致命地破坏了所显示的代码试图执行的操作。以下一种或多种原因的组合导致了您观察到的崩溃。

\n
struct Table\n{\n     std::vector<int> ids;\n};\n
Run Code Online (Sandbox Code Playgroud)\n

第一个原因是这不是一个可简单复制的对象,因此任何尝试逐字节复制它(如所示代码尝试执行的操作)都会导致未定义的行为。

\n
storage.insert(storage.end(), bytes, bytes + sizeof(Table));\n
Run Code Online (Sandbox Code Playgroud)\n

第二个原因是它sizeof()是一个编译时间常数。你可能不知道sizeof这个Table对象的 总是相同的,无论其向量是否为空或包含 \xcf\x80 的前十亿位数字。因此,由于这个根本原因,以这种方式将整个对象复制到字节缓冲区的尝试失败了。

\n
auto convert = [](Table x){ return reinterpret_cast<std::byte*>(&x); };\n
Run Code Online (Sandbox Code Playgroud)\n

第三个原因是,出于所有实际目的,该 lambda 在其参数方面与任何其他函数相同:x一旦该函数返回,其参数就会超出范围并被销毁。

\n

当函数接收参数时,该参数就像函数中的本地对象一样,并且是调用者传递给它的任何内容的副本,并且与函数中的所有其他本地对象一样,它在函数返回时被销毁。该函数最终返回一个指向已销毁对象的指针,并且该指针的后续使用也成为未定义的行为。

\n

总之,不幸的是,所显示的代码试图做的事情违背了 C++ 的多个核心基础原理,并且由于这些原因之一或某些原因的组合而导致崩溃;C++ 根本不以这种方式工作。

\n
\n

如果我内联转换函数,代码可以正常工作

\n
\n

如果通过反复试验,您想出了一些编译器选项的组合或外观调整,可以避免崩溃,出于某种神奇的原因,它不会解决任何潜在的问题,并且在稍后的某个时候无论如何,你都会崩溃,或者代码将无法正常工作。有保证。

\n
\n

我该如何解决这个问题?

\n
\n

解决此问题的唯一方法是,不要执行任何此操作。您还表示您想要做的只是“将不同类型的多个向量存储在同一个容器中”。这恰好是std::variant可以轻松、安全地处理的问题,因此您需要对此进行研究。

\n