C++ 程序员应该避免使用 memset 吗?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1975916/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Should C++ programmer avoid memset?
提问by Jichao
I heard a saying that c++ programmers should avoid memset,
我听说c++程序员应该避免memset,
class ArrInit {
//! int a[1024] = { 0 };
int a[1024];
public:
ArrInit() { memset(a, 0, 1024 * sizeof(int)); }
};
so considering the code above,if you do not use memset,how could you make a[1..1024] filled with zero?Whats wrong with memset in C++?
所以考虑到上面的代码,如果你不使用memset,你怎么能让[1..1024]充满零?C++中的memset有什么问题?
thanks.
谢谢。
回答by Charles Salvia
In C++ std::fill
or std::fill_n
may be a better choice, because it is generic and therefore can operate on objects as well as PODs. However, memset
operates on a raw sequence of bytes, and should therefore never be used to initialize non-PODs. Regardless, optimized implementations of std::fill
may internally use specialization to call memset
if the type is a POD.
在 C++ 中std::fill
或者std::fill_n
可能是更好的选择,因为它是通用的,因此可以对对象和 POD 进行操作。但是,memset
对原始字节序列进行操作,因此不应用于初始化非 POD。无论如何,如果类型是 POD , 的优化实现std::fill
可能会在内部使用专门化来调用memset
。
回答by Charles Salvia
The issue is not so much using memset() on the built-in types, it is using them on class (aka non-POD) types. Doing so will almost always do the wrong thing and frequently do the fatal thing - it may, for example, trample over a virtual function table pointer.
问题不是在内置类型上使用 memset(),而是在类(又名非 POD)类型上使用它们。这样做几乎总是会做错事并经常做致命的事情——例如,它可能会践踏虚函数表指针。
回答by UncleBens
Zero-initializing should look like this:
零初始化应如下所示:
class ArrInit {
int a[1024];
public:
ArrInit(): a() { }
};
As to using memset, there are a couple of ways to make the usage more robust (as with all such functions): avoid hard-coding the array's size and type:
至于使用 memset,有几种方法可以使使用更加健壮(与所有此类函数一样):避免对数组的大小和类型进行硬编码:
memset(a, 0, sizeof(a));
For extra compile-time checks it is also possible to make sure that a
indeed is an array (so sizeof(a)
would make sense):
对于额外的编译时检查,还可以确保它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)); }
But for non-character types, I'd imagine the only value you'd use it to fill with is 0, and zero-initialization should already be available in one way or another.
但是对于非字符类型,我想您使用它填充的唯一值是 0,并且零初始化应该已经以一种或另一种方式可用。
回答by AnT
What's wrong with memset
in C++ is mostly the same thing that's wrong with memset
in C. memset
fills memory region with physical zero-bit pattern, while in reality in virtually 100% of cases you need to fill an array with logical zero-values of corresponding type. In C language, memset
is only guaranteed to properly initialize memory for integer types (and its validity for allinteger types, as opposed to just char types, is a relatively recent guarantee added to C language specification). It is not guaranteed to properly set to zero any floating point values, it is not guaranteed to produce proper null-pointers.
memset
C++ 中的问题与C 中的问题大致相同memset
。memset
用物理零位模式填充内存区域,而实际上在几乎 100% 的情况下,您需要用相应类型的逻辑零值填充数组。在 C 语言中,memset
仅保证为整数类型正确初始化内存(并且它对所有整数类型的有效性,而不仅仅是 char 类型,是添加到 C 语言规范中的一个相对较新的保证)。不能保证将任何浮点值正确设置为零,也不能保证产生正确的空指针。
Of course, the above might be seen as excessively pedantic, since the additional standards and conventions active on the given platform might (and most certainly will) extend the applicability of memset
, but I would still suggest following the Occam's razor principle here: don't rely on any other standards and conventions unless you really really have to. C++ language (as well a C) offers several language-level features that let you safely initialize your aggregate objects with proper zero values of proper type. Other answers already mentioned these features.
当然,上述内容可能会被视为过于迂腐,因为在给定平台上活跃的其他标准和约定可能(并且肯定会)扩展 的适用性memset
,但我仍然建议在这里遵循奥卡姆剃刀原则:不要除非您真的必须依赖任何其他标准和约定。C++ 语言(以及 C)提供了多种语言级别的功能,让您可以使用正确类型的正确零值安全地初始化聚合对象。其他答案已经提到了这些功能。
回答by jcoder
It is "bad" because you are not implementing your intent.
这是“坏的”,因为你没有实现你的意图。
Your intent is to set each value in the array to zero and what you have programmed is setting an area of raw memory to zero. Yes, the two things have the same effect but it's clearer to simply write code to zero each element.
您的意图是将数组中的每个值设置为零,而您所编程的内容是将原始内存区域设置为零。是的,这两件事具有相同的效果,但简单地编写代码将每个元素归零会更清晰。
Also, it's likely no more efficient.
此外,它可能不会更有效率。
class ArrInit
{
public:
ArrInit();
private:
int a[1024];
};
ArrInit::ArrInit()
{
for(int i = 0; i < 1024; ++i) {
a[i] = 0;
}
}
int main()
{
ArrInit a;
}
Compiling this with visual c++ 2008 32 bit with optimisations turned on compiles the loop to -
使用visual c ++ 2008 32位并打开优化来编译它,将循环编译为-
; Line 12
xor eax, eax
mov ecx, 1024 ; 00000400H
mov edi, edx
rep stosd
Which is pretty much exactly what the memset would likely compile to anyway. But if you use memset there is no scope for the compiler to perform further optimisations, whereas by writing your intent it's possible that the compiler could perform further optimisations, for example noticing that each element is later set to something else before it is used so the initialisation can be optimised out, which it likely couldn't do nearly as easily if you had used memset.
无论如何,这几乎正是 memset 可能编译的内容。但是,如果您使用 memset,则编译器没有执行进一步优化的余地,而通过编写您的意图,编译器可能会执行进一步的优化,例如注意到每个元素在使用之前都被设置为其他内容,因此初始化可以优化,如果你使用了 memset,它可能不会那么容易做到。
回答by user1920453
This is an OLD thread, but here's an interesting twist:
这是一个旧线程,但这里有一个有趣的转折:
class myclass
{
virtual void somefunc();
};
myclass onemyclass;
memset(&onemyclass,0,sizeof(myclass));
works PERFECTLY well!
效果很好!
However,
然而,
myclass *myptr;
myptr=&onemyclass;
memset(myptr,0,sizeof(myclass));
indeed sets the virtuals (i.e somefunc() above) to NULL.
确实将虚拟(即上面的 somefunc() )设置为 NULL。
Given that memset is drastically faster than setting to 0 each and every member in a large class, I've been doing the first memset above for ages and never had a problem.
考虑到 memset 比将大类中的每个成员设置为 0 快得多,我多年来一直在做上面的第一个 memset 并且从来没有遇到过问题。
So the really interesting question is how come it works? I suppose that the compiler actually starts to set the zero's BEYOND the virtual table... any idea?
所以真正有趣的问题是它是如何工作的?我想编译器实际上开始在虚拟表之外设置零......知道吗?
回答by rui
Your code is fine. I thought the only time in C++ where memset is dangerous is when you do something along the lines of:YourClass instance; memset(&instance, 0, sizeof(YourClass);
.
你的代码没问题。我想在C ++中唯一的一次,其中memset的是危险的是当你沿着线的东西:YourClass instance; memset(&instance, 0, sizeof(YourClass);
。
I believe it might zero out internal data in your instance that the compiler created.
我相信它可能会将编译器创建的实例中的内部数据归零。
回答by Adrian McCarthy
In addition to badness when applied to classes, memset
is also error prone. It's very easy to get the arguments out-of-order, or to forget the sizeof
portion. The code will usually compile with these errors, and quietly do the wrong thing. The symptom of the bug might not manifest until much later, making it difficult to track down.
除了应用于类时的糟糕之外,memset
还容易出错。很容易让参数乱序,或者忘记sizeof
部分。代码编译时通常会出现这些错误,并悄悄地做错事。该错误的症状可能要到很晚才会出现,因此很难追踪。
memset
is also problematic with lots of plain types, like pointers and floating point. Some programmers set all bytes to 0, assuming the pointers will then be NULL and floats will be 0.0. That's not a portable assumption.
memset
很多普通类型也有问题,比如指针和浮点数。一些程序员将所有字节设置为 0,假设指针将为 NULL,而浮点数将为 0.0。这不是一个可移植的假设。
回答by Charles Eli Cheese
There's no real reason to not use it except for the few cases people pointed out that no one would use anyway, but there's no real benefit to using it either unless you are filling memguards or something.
没有真正的理由不使用它,除非人们指出无论如何没人会使用它的少数情况,但使用它也没有真正的好处,除非你填充 memguards 或其他东西。
回答by Kit10
The short answer would be to use an std::vector with an initial size of 1024.
简短的回答是使用初始大小为 1024 的 std::vector。
std::vector< int > a( 1024 ); // Uses the types default constructor, "T()".
The initial value of all elements of "a" would be 0, as the std::vector(size) constructor (as well as vector::resize) copies the value of the default constructor for all elements. For built-in types (a.k.a. intrinsic types, or PODs), you are guaranteed the initial value to be 0:
"a" 的所有元素的初始值为 0,因为 std::vector(size) 构造函数(以及 vector::resize)复制所有元素的默认构造函数的值。对于内置类型(又名内在类型,或 POD),保证初始值为 0:
int x = int(); // x == 0
This would allow the type that "a" uses to change with minimal fuss, even to that of a class.
这将允许“a”使用的类型以最小的麻烦改变,甚至是类的类型。
Most functions that take a void pointer (void*) as a parameter, such as memset, are not type safe. Ignoring an object's type, in this way, removes all C++ style semantics objects tend to rely on, such as construction, destruction and copying. memset makes assumptions about a class, which violates abstraction (not knowing or caring what is inside a class). While this violation isn't always immediately obvious, especially with intrinsic types, it can potentially lead to hard to locate bugs, especially as the code base grows and changes hands. If the type that is memset is a class with a vtable (virtual functions) it will also overwrite that data.
大多数将 void 指针 (void*) 作为参数的函数,例如 memset,都不是类型安全的。以这种方式忽略对象的类型会删除对象倾向于依赖的所有 C++ 样式语义,例如构造、销毁和复制。memset 对类做出假设,这违反了抽象(不知道或关心类中的内容)。虽然这种违规并不总是很明显,尤其是对于内在类型,但它可能会导致难以定位错误,尤其是随着代码库的增长和易手。如果 memset 类型是具有 vtable(虚拟函数)的类,它也会覆盖该数据。