C ++程序员应该避免memset?

我听到一个说c ++程序员应该避免memset,

class ArrInit { //! int a[1024] = { 0 }; int a[1024]; public: ArrInit() { memset(a, 0, 1024 * sizeof(int)); } }; 

所以考虑上面的代码,如果你不使用memset,你怎么能做一个[1..1024]填零?memset在C ++中有什么错误?

谢谢。

这个问题不是在内buildtypes上使用memset(),而是在类(又名非POD)types上使用它们。 这样做几乎总会做错误的事情,经常做致命的事情 – 例如,它可能会践踏虚拟函数表指针。

在C ++ std::fillstd::fill_n可能是一个更好的select,因为它是通用的,因此可以对对象和POD进行操作。 但是, memset在原始字节序列上运行,因此不应用于初始化非POD。 无论如何,如果types是POD, std::fill优化实现可能会在内部使用专门化来调用memset

零初始化应该是这样的:

 class ArrInit { int a[1024]; public: ArrInit(): a() { } }; 

至于使用memset,有几种方法使用法更加健壮(如同所有这些函数一样):避免硬编码数组的大小和types:

 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)); } 

但是对于非字符types,我想可以用它来填充的唯一值是0,并且零初始化应该已经可以用这种或那种方式。

这是“坏”,因为你没有实现你的意图。

您的目的是将数组中的每个值设置为零,并且您已经编程的是将原始内存区域设置为零。 是的,这两个东西具有相同的效果,但是只需编写代码来清零每个元素就会更清楚。

而且,它可能不会更有效率。

 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的话,它可能做不到那么容易。

memset在C ++中出了什么问题,与memset在C. memset中的错误大部分是相同的, memset填充了物理零位模式的内存区域,而实际上,在几乎100%的情况下,您需要用逻辑零值对应的types。 在C语言中, memset只保证为整数types正确初始化内存(对于所有整数types来说,它的有效性与chartypes相反,是对C语言规范的一个相对较新的保证)。 不能保证将任何浮点值设置为零,但不能保证产生正确的空指针。

当然,上述可能被认为是过于迂腐,因为在特定平台上活动的附加标准和惯例可能(并且肯定会)扩展memset的适用性,但我仍然build议遵循Occam的剃刀原则:除非你真的必须依靠任何其他的标准和惯例。 C ++语言(以及C语言)提供了几种语言级别的function,可以让您安全地初始化具有适当types的适当零值的聚合对象。 其他答案已经提到这些function。

这是一个旧的线程,但这是一个有趣的转折:

 class myclass { virtual void somefunc(); }; myclass onemyclass; memset(&onemyclass,0,sizeof(myclass)); 

完美的作品!

然而,

 myclass *myptr; myptr=&onemyclass; memset(myptr,0,sizeof(myclass)); 

的确将虚拟(即上面的somefunc())设置为NULL。

鉴于memset的速度比设置为大类中的每个成员都要快得多,所以我一直在做上面的第一个memset,从来没有遇到过问题。

所以真正有趣的问题是它是如何起作用的? 我想,编译器实际上开始设置零的BEYOND虚拟表…任何想法?

你的代码很好。 我认为memset是危险的C ++唯一的一次是当你做一些事情的时候:
YourClass instance; memset(&instance, 0, sizeof(YourClass); YourClass instance; memset(&instance, 0, sizeof(YourClass);

我相信它可能会将编译器创build的实例中的内部数据清零。

当应用于类时,除了糟糕之外, memset也容易出错。 乱序或者忘记sizeof部分是非常容易的。 代码通常会编译这些错误,并悄悄地做错误的事情。 该错误的症状可能不会performance出来,直到很久以后,这使得难以追踪。

memset也是很多普通types的问题,比如指针和浮点。 一些程序员将所有字节设置为0,假设指针将为NULL,浮点数为0.0。 这不是一个便携式的假设。

没有什么真正的理由不使用它,除非less数人指出没有人会使用它,但是除非你正在填写保护程序或什么东西,否则没有任何真正的好处。

简短的答案是使用初始大小为1024的std :: vector。

 std::vector< int > a( 1024 ); // Uses the types default constructor, "T()". 

因为std :: vector(size)构造函数(以及vector :: resize)复制了所有元素的默认构造函数的值,所以“a”的所有元素的初始值都是0。 对于内置types(又名内在types或POD),保证初始值为0:

 int x = int(); // x == 0 

这样可以让“a”的types用最小的改动来改变,甚至是一个类的改变。

大多数将void指针(void *)作为参数的函数(如memset)都不是types安全的。 忽略对象的types,这样就删除了所有依赖的C ++样式语义对象,如构造,销毁和复制。 memset对一个类进行假设,这违反了抽象(不知道或关心类内部的东西)。 虽然这种违规并不总是立即显而易见的,尤其是对于内部types而言,它可能导致难以定位错误,特别是随着代码库的增长和易手。 如果memset的types是一个具有虚表(虚函数)的类,它也会覆盖这些数据。

在C ++中,你应该使用新的。 在你的例子中使用简单数组的情况下,使用它没有实际的问题。 但是,如果你有一个类的数组,并用memset来初始化它,那么你就不能正确地构造类。

考虑一下:

 class A { int i; A() : i(5) {} } int main() { A a[10]; memset (a, 0, 10 * sizeof (A)); } 

每个这些元素的构造函数都不会被调用,所以成员variablesi将不会被设置为5.如果您使用new来代替:

  A a = new A[10]; 

比数组中的每个元素都有其构造函数调用,我将被设置为5。