std::vector::reserve 之后分配内存的 memset
memset of allocated memory after std::vector::reserve
关于这个主题已经有一个密切相关的问题 here,但是这个问题的争议很大,相关的讨论让我有点困惑。
那么下面的思路对吗?
我的情况是这样的:
我有一个使用块来存储数据的数据结构。我想使用 std::vector<ChunkT> myChunks; myChunks.reserve(1000000);
之类的方法预分配大量块,并在需要时使用 ChunkT* newChunk = &myChunks.emplace_back();
获取一个无需分配的新块。我希望新块被零初始化,但我更喜欢在保留内存后直接使用 memset
进行初始化,而不是在获取它后一次初始化一个块。前提是 ChunkT
是 POD,例如struct {size_t keys[512]; size_t values[512];};
我不确定以下内容:
- 在
reserve
之后使用memset
对内存进行0初始化是否安全?
- 在使用
ChunkT* newChunk &myChunks.emplace_back()
获取我的块后,是否保证在 ChunkT 为 struct {size_t keys[512]; size_t values[512];};
的示例中我仍然有 0 初始化的内存?
关于 1.) 链接问题中的一位用户认为这是不安全的,因为该标准不保证 std::vector
实现可能会使用保留内存做什么(例如,将其用于内部簿记) . wpzdm 争辩说保留内存不会发生任何令人惊讶的事情。阅读所有相关讨论后,我现在认为仅访问保留内存中的对象是安全的,因为它们的生命周期已经开始(因为它们是 POD 并由向量的分配器分配),因此它们是完全有效的对象。然而,在内存成为“有效”范围的一部分之前,它们的内容在任何时候都无法保证,例如通过emplace_back
,因为标准没有说vector实现一定不能修改保留范围(所以2.)是No?)。但矢量实现也不能依赖于那些保留对象的内容,因为我们可以根据需要访问和更改它们。因此,无论是“内部簿记”还是设置调试标志来检测“有效”之外但在保留范围内或类似内容的越界访问,都不会严格符合标准,因为它可能会导致不允许的副作用。那么只有恶意或不符合规范的编译器才会修改保留范围?
如果我将 ChunkT
更改为 struct {size_t keys[512]={0}; size_t values[512]={0};};
那么 emplace_back
之后的对象内容是可以保证的,但是这次是因为初始化是通过构造进行的。此外,现在访问唯一保留的内存将是未定义的行为,因为对象的生命周期尚未开始。
- is it guaranteed that I still have 0-initialized memory in the example of ChunkT being struct {size_t keys[512]; size_t values[512];}; after fetching my chunk with ChunkT* newChunk &myChunks.emplace_back()?
emplace_back()
value 初始化对象,因此无论对象创建之前包含什么内存,都可以保证零初始化。
- is it safe to 0-initialize the memory using memset after reserve?
也许有用,但最好不要。通过 []
访问不存在的元素是 UB。
- is it guaranteed that I still have 0-initialized memory in the example of ChunkT being struct {size_t keys[512]; size_t values[512];}; after fetching my chunk with ChunkT* newChunk &myChunks.emplace_back()?
是的。在您的情况下,emplace_back()
所做的是通过 placement-new 构造一个 Chunk
,POD-类 将被零初始化。
参考:POD class initialized with placement new default initialized?
因此,您不必担心 memset 分配的内存为零。如有不妥请指正
关于这个主题已经有一个密切相关的问题 here,但是这个问题的争议很大,相关的讨论让我有点困惑。 那么下面的思路对吗?
我的情况是这样的:
我有一个使用块来存储数据的数据结构。我想使用 std::vector<ChunkT> myChunks; myChunks.reserve(1000000);
之类的方法预分配大量块,并在需要时使用 ChunkT* newChunk = &myChunks.emplace_back();
获取一个无需分配的新块。我希望新块被零初始化,但我更喜欢在保留内存后直接使用 memset
进行初始化,而不是在获取它后一次初始化一个块。前提是 ChunkT
是 POD,例如struct {size_t keys[512]; size_t values[512];};
我不确定以下内容:
- 在
reserve
之后使用memset
对内存进行0初始化是否安全? - 在使用
ChunkT* newChunk &myChunks.emplace_back()
获取我的块后,是否保证在 ChunkT 为struct {size_t keys[512]; size_t values[512];};
的示例中我仍然有 0 初始化的内存?
关于 1.) 链接问题中的一位用户认为这是不安全的,因为该标准不保证 std::vector
实现可能会使用保留内存做什么(例如,将其用于内部簿记) . wpzdm 争辩说保留内存不会发生任何令人惊讶的事情。阅读所有相关讨论后,我现在认为仅访问保留内存中的对象是安全的,因为它们的生命周期已经开始(因为它们是 POD 并由向量的分配器分配),因此它们是完全有效的对象。然而,在内存成为“有效”范围的一部分之前,它们的内容在任何时候都无法保证,例如通过emplace_back
,因为标准没有说vector实现一定不能修改保留范围(所以2.)是No?)。但矢量实现也不能依赖于那些保留对象的内容,因为我们可以根据需要访问和更改它们。因此,无论是“内部簿记”还是设置调试标志来检测“有效”之外但在保留范围内或类似内容的越界访问,都不会严格符合标准,因为它可能会导致不允许的副作用。那么只有恶意或不符合规范的编译器才会修改保留范围?
如果我将 ChunkT
更改为 struct {size_t keys[512]={0}; size_t values[512]={0};};
那么 emplace_back
之后的对象内容是可以保证的,但是这次是因为初始化是通过构造进行的。此外,现在访问唯一保留的内存将是未定义的行为,因为对象的生命周期尚未开始。
- is it guaranteed that I still have 0-initialized memory in the example of ChunkT being struct {size_t keys[512]; size_t values[512];}; after fetching my chunk with ChunkT* newChunk &myChunks.emplace_back()?
emplace_back()
value 初始化对象,因此无论对象创建之前包含什么内存,都可以保证零初始化。
- is it safe to 0-initialize the memory using memset after reserve?
也许有用,但最好不要。通过 []
访问不存在的元素是 UB。
- is it guaranteed that I still have 0-initialized memory in the example of ChunkT being struct {size_t keys[512]; size_t values[512];}; after fetching my chunk with ChunkT* newChunk &myChunks.emplace_back()?
是的。在您的情况下,emplace_back()
所做的是通过 placement-new 构造一个 Chunk
,POD-类 将被零初始化。
参考:POD class initialized with placement new default initialized?
因此,您不必担心 memset 分配的内存为零。如有不妥请指正