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