我听说c ++程序员应该避免使用memset,
class ArrInit {
//! int a[1024] = { 0 };
int a[1024];
public:
ArrInit() { memset(a, 0, 1024 * sizeof(int)); }
};
所以考虑到上面的代码,如果你不使用memset,怎么能把[1..1024]填充为0?在C ++中memset有什么问题?
感谢。
答案 0 :(得分:49)
在C ++中std::fill
或std::fill_n
可能是更好的选择,因为它是通用的,因此可以对对象和POD进行操作。但是,memset
对原始字节序列进行操作,因此不应该用于初始化非POD。无论如何,如果类型是POD,std::fill
的优化实现可以在内部使用特化来调用memset
。
答案 1 :(得分:44)
问题不是在内置类型上使用memset(),而是在类(也称为非POD)类型上使用它们。这样做几乎总是会做错事并经常做致命的事情 - 例如,它可能会践踏虚函数表指针。
答案 2 :(得分:23)
零初始化应如下所示:
class ArrInit {
int a[1024];
public:
ArrInit(): a() { }
};
至于使用memset,有几种方法可以使用法更加健壮(就像所有这些函数一样):避免硬编码数组的大小和类型:
memset(a, 0, sizeof(a));
对于额外的编译时检查,还可以确保a
确实是一个数组(因此sizeof(a)
是有意义的):
template <class T, size_t N>
size_t array_bytes(const T (&)[N]) //accepts only real arrays
{
return sizeof(T) * N;
}
ArrInit() { memset(a, 0, array_bytes(a)); }
但对于非字符类型,我认为你用它填充的唯一值是0,零初始化应该已经以这种或那种方式可用。
答案 3 :(得分:10)
C ++中memset
的错误与C中memset
的错误大致相同。memset
用物理零位模式填充内存区域,而实际上是100您需要使用相应类型的逻辑零值填充数组的百分比。在C语言中,memset
仅保证为整数类型正确初始化内存(并且所有整数类型的有效性,而不仅仅是char类型,是C中添加的相对较新的保证。语言规范)。不能保证将任何浮点值正确设置为零,不能保证产生正确的空指针。
当然,上述内容可能被视为过于迂腐,因为在给定平台上活跃的其他标准和约定可能(并且肯定会)扩展memset
的适用性,但我仍然建议遵循奥卡姆的剃刀原则在这里:除非你真的需要,否则不要依赖任何其他标准和惯例。 C ++语言(以及C语言)提供了几种语言级功能,使您可以使用正确类型的正确零值安全地初始化聚合对象。其他答案已经提到了这些功能。
答案 4 :(得分:8)
这是“糟糕的”,因为你没有实现你的意图。
您的目的是将数组中的每个值设置为零,并且您编写的内容是将原始内存区域设置为零。是的,这两件事具有相同的效果,但只需将代码编写为零,就可以更清楚了。
此外,它可能没有效率。
class ArrInit
{
public:
ArrInit();
private:
int a[1024];
};
ArrInit::ArrInit()
{
for(int i = 0; i < 1024; ++i) {
a[i] = 0;
}
}
int main()
{
ArrInit a;
}
使用visual c ++ 2008 32位进行编译并启用优化,将循环编译为 -
; Line 12
xor eax, eax
mov ecx, 1024 ; 00000400H
mov edi, edx
rep stosd
这几乎就是memset可能会编译的内容。但是如果你使用memset,那么编译器就没有空间来执行进一步的优化,而通过编写你的意图,编译器可能会执行进一步的优化,例如注意到每个元素在被使用之前都被设置为其他元素,所以可以优化初始化,如果你使用了memset,它可能不会那么容易。
答案 5 :(得分:1)
这是一个老线程,但这是一个有趣的转折:
class myclass
{
virtual void somefunc();
};
myclass onemyclass;
memset(&onemyclass,0,sizeof(myclass));
完美地工作!
然而,
myclass *myptr;
myptr=&onemyclass;
memset(myptr,0,sizeof(myclass));
确实将虚拟(即上面的somefunc())设置为NULL。
鉴于memset比大型班级中的每个成员设置为0要快得多,我已经做了上面的第一个memset多年,从来没有遇到过问题。
所以真正有趣的问题是它是如何运作的?我想编译器实际上开始设置零的BEYOND虚拟表...任何想法?
答案 6 :(得分:0)
你的代码很好。我认为在C ++中唯一一个memset很危险的时候是你做的事情:
YourClass instance; memset(&instance, 0, sizeof(YourClass);
。
我相信它可能会将编译器创建的实例中的内部数据归零。
答案 7 :(得分:0)
除了应用于课程时的不良之外,memset
也容易出错。很容易让参数无序,或忘记sizeof
部分。代码通常会编译出这些错误,并悄悄地做错了。这个错误的症状可能要到很晚才会显现出来,因此难以追踪。
memset
对于许多普通类型(如指针和浮点)也存在问题。一些程序员将所有字节设置为0,假设指针将为NULL并且浮点数将为0.0。这不是一个可移植的假设。
答案 8 :(得分:0)
没有真正的理由不使用它,除了少数情况下人们指出没有人会使用它,但除非你填充memguards或其他东西,否则使用它没有任何实际好处。
答案 9 :(得分:0)
简短的回答是使用初始大小为1024的std :: vector。
std::vector< int > a( 1024 ); // Uses the types default constructor, "T()".
“a”的所有元素的初始值将为0,因为std :: vector(size)构造函数(以及vector :: resize)复制所有元素的默认构造函数的值。对于内置类型(a.k.a。内在类型或POD),保证初始值为0:
int x = int(); // x == 0
这将允许“a”使用的类型以最小的麻烦改变,甚至是类的改变。
将void指针(void *)作为参数(例如memset)的大多数函数都不是类型安全的。以这种方式忽略对象的类型会删除所有依赖的C ++样式语义对象,例如构造,销毁和复制。 memset对类进行了假设,这违反了抽象(不知道或关心类中的内容)。虽然这种违规并不总是很明显,特别是对于内在类型,但它可能导致很难找到错误,特别是在代码库增长和易手时。如果memset的类型是带有vtable(虚函数)的类,它也会覆盖该数据。
答案 10 :(得分:-4)
在C ++中你应该使用new。对于像您的示例中的简单数组,使用它没有真正的问题。但是,如果你有一个类数组并使用memset来初始化它,你就不会正确地构造类。
考虑一下:
class A {
int i;
A() : i(5) {}
}
int main() {
A a[10];
memset (a, 0, 10 * sizeof (A));
}
每个元素的构造函数将不被调用,因此成员变量i将不会被设置为5.如果您使用new而不是:
A a = new A[10];
比数组中的每个元素都将调用其构造函数,并且我将设置为5.