memset()或值初始化零结构?
在Win32 API编程中,通常使用具有多个字段的C struct
。 通常只有其中的一些有意义的价值,所有其他的都必须被清除。 这可以通过两种方式来实现:
STRUCT theStruct; memset( &theStruct, 0, sizeof( STRUCT ) );
要么
STRUCT theStruct = {};
第二个变种看起来更清洁 – 这是一个单线,它没有任何参数,可能错误input,导致错误被种植。
与第一个变体相比,它有什么缺点吗? 使用哪个变体,为什么?
这两个构念在意义上有很大的不同。 第一个使用memset
函数,这是为了将内存的缓冲区设置为一定的值 。 第二个初始化一个对象 。 让我用一些代码来解释它:
让我们假设你有一个只有PODtypes成员的结构
struct POD_OnlyStruct { int a; char b; }; POD_OnlyStruct t = {}; // OK POD_OnlyStruct t; memset(&t, 0, sizeof t); // OK as well
在这种情况下写一个POD_OnlyStruct t = {}
或POD_OnlyStruct t; memset(&t, 0, sizeof t)
POD_OnlyStruct t; memset(&t, 0, sizeof t)
没有什么区别,因为我们这里唯一的区别是memset
被使用时alignment字节被设置为零值。 由于您无法正常访问这些字节,因此您没有任何区别。
另一方面,因为你已经把你的问题标记为C ++,所以让我们尝试另一个例子,成员types与POD不同 :
struct TestStruct { int a; std::string b; }; TestStruct t = {}; // OK { TestStruct t1; memset(&t1, 0, sizeof t1); // ruins member 'b' of our struct } // Application crashes here
在这种情况下,使用像TestStruct t = {}
这样的expression式是很好的,使用memset
就会导致崩溃。 下面是使用memset
时发生的情况 – 创build了一个TestStruct
types的对象,因此创build了std::string
types的对象,因为它是我们结构的一个成员。 接下来, memset
将对象b
所在的内存设置为某个值,例如零。 现在,一旦我们的TestStruct对象超出了范围,它将被销毁,并且当它成为std::string b
成员时,您将看到崩溃,因为该对象的所有内部结构都被memset
破坏了。
所以,现实情况是, 这些事情是非常不同的 ,虽然有时候在某些情况下需要将整个结构化为零,但是确保你明白你在做什么总是很重要的,而不是像我们这样犯一个错误第二个例子。
我的投票 – 仅在需要时才在对象上使用memset
,并在所有其他情况下使用默认的初始化x = {}
。
根据结构成员的不同,这两个变体不一定相同。 memset
会将结构设置为all-bits-zero,而值初始化会将所有成员初始化为零。 C标准保证这些只对整型是相同的,而不是浮点值或指针。
而且,一些API要求结构真的被设置为全比特零。 例如,Berkeley套接字API使用结构多态,并且重要的是将整个结构真正设置为零,而不仅仅是显而易见的值。 API文档应该说明结构是否真的需要全部为零,但可能是不足的。
但是,如果这些或者类似的情况都不适用,那就取决于你。 当定义结构时,我会更愿意初始化值,因为这更清楚地传达了意图。 当然,如果你需要将现有的结构归零, memset
是唯一的select(除了手动将每个成员初始化为零,但通常不会这样做,特别是对于大型结构)。
如果你的结构包含如下的东西:
int a; char b; int c;
然后在“b”和“c”之间插入填充字节。 memset()会将这些值清零,反之则不会,所以会有3个字节的垃圾(如果你的整数是32位的话)。 如果您打算使用您的结构来读取/写入文件,这可能是重要的。
我会使用值初始化,因为它看起来干净,并且不像您提到的那样容易出错。 我没有看到这样做的任何缺点。
虽然你可能依赖memset
来清零结构。
这并不常见,但我想第二种方法也有将浮点数初始化为零的好处。 而做一个memset肯定不会
在某些编译器中STRUCT theStruct = {};
会转换成memset( &theStruct, 0, sizeof( STRUCT ) );
在可执行文件中。 一些C函数已经链接到进行运行时设置,因此编译器可以使用像memset / memcpy这样的库函数。
值初始化,因为它可以在编译时完成。
也正确地0初始化所有的PODtypes。
memset()在运行时完成。
如果结构不是POD,也可以使用memset()。
不正确地初始化(为零)非inttypes。
如果有很多指针成员,并且将来可能会添加更多,则可以使用memset。 结合适当的assert(struct->member)
调用,你可以避免随机崩溃,试图推迟一个你忘记初始化的坏指针。 但如果你不像我一样健忘,那么成员初始化可能是最好的!
但是 ,如果您的结构被用作公共API的一部分,您应该获得客户端代码以使用memset作为要求。 这有助于未来的校对,因为您可以添加新成员,并且客户端代码将在memset调用中自动将它们清空,而不是将它们置于(可能是危险的)未初始化状态。 例如,这是您在处理套接字结构时所要做的。