在Win32 API编程中,通常将Cstruct与多个字段一起使用。通常,其中只有几个具有有意义的值,而所有其他值都必须归零。这可以通过以下两种方式之一来实现:
STRUCT theStruct;
memset( &theStruct, 0, sizeof( STRUCT ) );
要么
STRUCT theStruct = {};
第二个变体看起来更干净-它是单线的,它没有任何可能被错误键入并导致植入错误的参数。
与第一个变体相比,它有什么缺点吗?使用哪个变体,为什么?
在Win32 API编程中,通常将Cstruct与多个字段一起使用。通常,其中只有几个具有有意义的值,而所有其他值都必须归零。这可以通过以下两种方式之一来实现:
STRUCT theStruct;
memset( &theStruct, 0, sizeof( STRUCT ) );
要么
STRUCT theStruct = {};
第二个变体看起来更干净-它是单线的,它没有任何可能被错误键入并导致植入错误的参数。
与第一个变体相比,它有什么缺点吗?使用哪个变体,为什么?
Answers:
这两个结构在含义上有很大不同。第一个使用一个memset函数,该函数旨在将内存缓冲区设置为某个值。第二个初始化一个对象。让我用一些代码来解释一下:
假设您有一个仅包含POD类型成员的结构(“普通旧数据”-请参阅C ++中的POD类型是什么?)
struct POD_OnlyStruct
{
int a;
char b;
};
POD_OnlyStruct t = {}; // OK
POD_OnlyStruct t;
memset(&t, 0, sizeof t); // OK as well
在这种情况下,写aPOD_OnlyStruct t = {}或POD_OnlyStruct t; memset(&t, 0, sizeof t)没有什么区别,因为我们这里唯一的区别是在使用情况下将对齐字节设置为零值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
在这种情况下,使用like这样的表达式TestStruct t = {}会很好,而memset在其上使用则会导致崩溃。如果您使用,就会发生以下情况memset-TestStruct创建了一个类型的对象,因此创建了一个类型的对象std::string,因为它是我们结构的成员。接下来,memset将对象所在的内存b设置为某个值,例如零。现在,一旦我们的TestStruct对象超出范围,它就会被销毁,当轮到它的成员时,std::string b您会看到崩溃,因为该对象的所有内部结构都被破坏了memset。
因此,现实是,这些事情非常不同,尽管有时memset在某些情况下您需要将整个结构归零,但确保您了解自己在做什么,而不是像我们在第二篇文章中那样犯错,始终很重要例。
我的投票-仅在需要时才memset在对象上使用,在所有其他情况下都使用默认初始化。x = {}
std::string。
if T is a (possibly cv-qualified) non-union class type, each non-static data member and each base-class subobject is zero-initialized and padding is initialized to zero bits;
根据结构成员的不同,两个变体不一定等效。memset会将结构设置为全零位,而值初始化会将所有成员初始化为零值。C标准保证这些仅对于整数类型是相同的,而不是对于浮点值或指针。
此外,某些API要求将结构真正设置为全零位。例如,Berkeley套接字API多态使用结构,重要的是将整个结构真正设置为零,而不仅仅是将表面上的值设置为零。API文档应说明该结构是否确实需要为全零位,但可能会有所不足。
但是,如果这些或类似情况均不适用,则取决于您。在定义结构时,我更喜欢值初始化,因为这样可以更清晰地传达意图。当然,如果您需要将现有结构归零,那memset是唯一的选择(好吧,除了手动将每个成员初始化为零之外,通常不会这样做,尤其是对于大型结构而言)。
如果您的结构包含以下内容:
int a;
char b;
int c;
然后将填充字节插入在“ b”和“ c”之间。memset()会将那些值归零,否则将不会,因此将有3个字节的垃圾(如果您的int是32位)。如果打算使用结构从文件读取/写入,这可能很重要。
我将使用值初始化,因为它看起来很干净,并且您提到的错误更少。我认为这样做没有任何缺点。
memset不过,在使用该结构后,您可能需要将其归零。
值初始化,因为它可以在编译时完成。
同样正确地为0初始化所有POD类型。
memset()在运行时完成。
如果该结构不是POD,也可能会使用memset()。
没有正确初始化(为零)非int类型。
如果有很多指针成员,并且您将来可能会添加更多成员,则可以使用memset。结合适当的assert(struct->member)调用,您可以避免尝试参考忘记初始化的错误指针而导致随机崩溃。但是,如果您不像我这样健忘,那么成员初始化可能是最好的!
但是,如果您的结构被用作公共API的一部分,则应获取客户端代码以使用memset作为要求。这有助于将来进行证明,因为您可以添加新成员,并且客户端代码将在memset调用中自动将它们清空,而不是将它们置于(可能是危险的)未初始化状态。例如,这就是使用套接字结构时要做的事情。
memset将以错误的结构大小进行调用。如果客户端代码被重新编译,则需要访问具有结构定义的更新头文件,以使memset值初始化或工作。(客户端和库确实需要对表示空指针的方式有一个一致的概念,因此,如果API建议memset,则应针对全零位进行检查,而不是针对NULL。)