使用 memset 初始化包含数组的结构向量

Posted

技术标签:

【中文标题】使用 memset 初始化包含数组的结构向量【英文标题】:using memset to initialize vector of structure which contains array 【发布时间】:2016-05-08 13:45:55 【问题描述】:

我有以下结构

struct a 
     int array[20]
     int array2[45]

我已经创建了这个结构的向量

vector<a> vec;

我用过这个 vec。现在我想初始化(设置向量元素中对象内的所有数组值)为零。我该怎么办?

【问题讨论】:

是什么阻止了您阅读std::vector 文档?你为什么决定使用memset?得出这个结论后你做了什么尝试? @LightnessRacesinOrbit:std::vector 通常会为其包含的对象调用默认构造函数。因为这个结构只是内置类型,所以元素不会被零初始化。 @MartinBonner:是的,我知道这一点。 OP 需要进行一些研究以找出如何将他的元素归零。这个问题展示了零研究工作。我正在尝试提示 OP进行一些研究 为什么你认为他没有?使用零初始化对象的副本构造向量、为结构提供默认构造函数和 memset 都是可行的方法 - memset 可能是最好的。 @MartinBonner std::vector 将对其元素进行值初始化,而不是默认初始化它们。所以它们将被零初始化。您可以通过阅读一些文档来了解这一点。 【参考方案1】:

阅读 cmets,您尚未显示填充向量的代码。现在您想重用存储而不是创建一个新存储,并且您想重新初始化它。

任何 C++ 容器的任何算法的规范答案都可能在标准算法中找到。在这种情况下,std::fill。以您喜欢的方式初始化您的结构,并将其复制到您的向量中。沿着这些思路:

a A = 0;
std::fill(vec.begin(), vec.end(), A);

如果您想说这还不够快,请检查一下。我想你会发现它非常有效。上面的代码绝对安全,适用于fill 的任何正确初始化的参数。我怀疑它是否可以在不对a 的实现做出一些假设的情况下做得更快。

【讨论】:

【参考方案2】:

事实证明,这是一个比最初看起来更有趣的问题。

tl;dr:如果您使用的是 C++03 或更高版本的编译器,则无需费心。

您需要了解value initializationdefault initialization 之间的区别。基本上值初始化会将所有元素设置为零,而默认初始化将使它们完全独立。如果结构的任何默认元素(递归)具有用户定义的默认构造函数,则值和默认初始化都会调用它。

请注意,值初始化memset为零好,因为

它将调用默认构造函数 它将正确地初始化浮点(到 0.0)和指针(到 NULL)。尽管memset可能会在您的实现中这样做,但不能保证这样做。

创建带有 n 个元素的向量的正常方法是调用:

std::vector<a> vec(n);

C++98

这会调用

std::vector<a>::vector(size_type count, 
                       const T& value = T(),
                       const Allocator& alloc = Allocator());

value 对象将被默认构造,您需要以某种方式初始化元素。做到这一点的最佳方法是提供要复制的正确值初始化值。所以:

const static a azeroed;  // Because this is static, it will be value initialized
std::vector<a> vec(20,azeroed);

技术说明:C++98标准不包含“值初始化”一词,但azeroed的初始化是相同的。

C++03

调用了相同的向量构造函数,但从 C++03 开始​​,value 参数值已初始化(所以花园里的一切都是美好的)。

C++11

打电话给

std::vector<a>::vector(size_type count);

哪个值直接初始化元素。

C++14

来电

std::vector<a>::vector(size_type count, const Allocator& alloc = Allocator());

(基本上,他们意识到他们忘记了分配器参数)。这里有一个非常细微的区别,因为元素是通过调用Allocator::construct 构造的,虽然默认分配器会初始化元素,但可以提供一个自定义版本,而不是(参见this answer) .如果你这样做,你几乎肯定知道你在做什么。

结论

除非你使用的是真正的 C++98 编译器,否则你不需要调用 memset 向向量构造函数提供显式初始化值比调用 memset 更安全。 memset 可能无法正确初始化非整数内置值(尽管它可能会)。 memset 肯定会破坏任何合适的构造函数。这是一个巨大的维护隐患。如果维护程序员更改结构使其不再是 POD,代码仍将编译 - 它只会做错事。 只需为结构体提供一个适当的默认构造函数,就有很多话要说,这样您就不必担心是否有任何元素已初始化,即使您有一个本地副本。

【讨论】:

这样做的问题是,如果你改变了类型,可能会使结构不再是 POD,这只会默默地继续将所有内容归零,这可能是一个严重的错误。我发现其中一个曾经在生产中造成多年的间歇性崩溃,使客户损失了数千美元。更喜欢 C++ 中的类型安全解决方案,尤其是在教学时。 这不是一个很好的例子。所示的初始化导致所有元素的初始化数据为零,而无需调用memset @juanchopanza: 章节和诗句好吗?给定结构的定义,默认构造 不会 零初始化它。 (还是我弄错了?) 正如我在另一条评论中所说,std::vector 值初始化元素。 @MartinBonner,对于特定的参考,[vector.cons] 表示默认插入的元素。 [container.requirements.general] 将其澄清为allocator_traits&lt;A&gt;::construct(m, p)。从那里,[allocator.traits.members] 将其转换为a.construct(p, std::forward&lt;Args&gt;(args)...) (因为这是默认分配器),[allocator.members] 说这是::new((void *)p) U(std::forward&lt;Args&gt;(args)...),这是当 args 包为空时的值初始化。这有点迂回,并且可以随着分配器而改变,但几乎在所有情况下都应该如此。

以上是关于使用 memset 初始化包含数组的结构向量的主要内容,如果未能解决你的问题,请参考以下文章

使用 memset 将结构体数组及其成员初始化为零

关于memset()

使用 memset 初始化指针元素的二维数组

将向量初始化添加到结构构造函数

c++中用memset初始化数组可以初始化任何值吗?

memset与初始化