使用结构初始化({ ...})比使用memset等方式更好吗?

9

代码:

WINDOWPLACEMENT wplcmt = {sizeof(WINDOWPLACEMENT)};

看起来比这个干净多了:

WINDOWPLACEMENT wplcmt;
memset(&wplcmt, 0, sizeof(WINDOWPLACEMENT));
wplcmt.length = sizeof(WINDOWPLACEMENT);

这个东西的汇编输出也很好,对于更长的结构,MSVC甚至使用memset而不是xor eax, eaxmov。从标准的角度来看,这看起来也还不错。但我仍然担心边界情况,在结构体没有紧密打包时,比如#pragma pack(128),突然间Windows决定进行结构体的memcmp时会发生什么。
那么使用这种语法好还是不好呢? 使用这样的初始化方法是否是一个好习惯?

2
这听起来像是一个钓鱼问题。 - Cheers and hth. - Alf
在我看来两者都可以,但为什么Windows(或其他任何东西)要对结构执行memcmp呢?对于具有未使用位的结构执行memcmp显然是不明智的。没有要求这些未使用的填充字节为零(或任何其他值),因此考虑它们的任何比较都将无效。是否有实际执行这样做的地方,还是你只是“以防万一”担心呢? - Leo Davidson
一旦代码离开你的手,你就无法确定接下来会发生什么。因此,我在想如何编写更干净、更健壮的代码。 - Coder
5个回答

8
你展示的第二段代码,
WINDOWPLACEMENT wplcmt;
memset(&wplcmt, 0, sizeof(WINDOWPLACEMENT));
wplcmt.length = sizeof(WINDOWPLACEMENT);

这简直是糟糕透顶。混淆、低效、冗长,你把它们都塞进去了。

第一段代码片段,

WINDOWPLACEMENT wplcmt = {sizeof(WINDOWPLACEMENT)};

除了混淆之外,这是首选的方式,除非您想要:
- 浪费更多时间编写不必要的代码, - 使读者花费更多时间阅读和不必要地分析您冗长的代码, - 获得效率更低的执行,并且 - 提供漏洞入口。
顺便问一下,你使用的混淆名称"wplcmt"是什么意思?
为什么要混淆名称?
你的问题是真实的还是简单地在捣乱?
祝好!

3
你的回答(在事实方面)很好,然而,你不应该无缘无故地指责别人。因此,你的回答无法赢得我的投票... - jpalecek
@疯子:如果可以的话,尽量避免使用检查填充字节是否为零或表现出其他未定义行为的库。 :-) - Cheers and hth. - Alf
@jpalecek:我并不是在指责,只是在询问,而且这并不是没有原因的,正如所述的那样。请考虑:一个“疯子”呈现出类似于微软低级C代码的C++代码,并询问更正常的代码相比有多“邪恶”,给人留下不能更好的印象。对我来说,这听起来像是在钓鱼。 - Cheers and hth. - Alf
1
C/C++是因为尽管我尽可能多地编写C ++,但没有人将WINAPI转换为C ++,至少据我所知。因此,如果有人知道一个干净的C ++解决方案,那就欢迎。关于更正常的问题,到目前为止,我们有两个完全相反的答案,所以即使我也喜欢你提出的解决方案,但我还没有被说服它是好的。 - Coder
@Alf:这些命名约定和memset混乱已经成为Windows多年的惯用做法。由于您的帖子毫无必要地充满敌意,我不能给+1。您是正确的,但态度过于不友好。 - greyfade
显示剩余3条评论

7

使用memset函数。这样每个人都能立即看出代码的作用,而且很不可能有任何意外的副作用。


+1。我同意。特别是因为这是初始化本地Windows结构的长期习语。 - André Caron
如果我们关心“惯用语”和本地Windows特定的话,难道不应该使用ZeroMemory()而不是memset()吗? :) - Karl Knechtel
如果结构体具有指针类型且指针不是所有位都为零,或者浮点值也可能不是所有位都为零,那该怎么办? - Coder
1
然后我们之后也会显式地设置这些值。给定 Windows 类的文档将告诉我们应该如何初始化它。 - Karl Knechtel

1

这种初始化我经常与之斗争。在C99中,可以这样做:

WINDOWPLACEMENT wplcmt = {.length = sizeof(wplcmt), .showCmd = SW_SHOW};

其他值都被初始化为零。

在 G++ 中,您可以执行以下操作:

WINDOWPLACEMENT wplcmt = {length: sizeof(wplcmt), showCmd: SW_SHOW};

最后,在C++中,您可以选择初始化所有成员,或者希望您能正确地获取成员顺序,如下所示:
WINDOWPLACEMENT wplcmt = {sizeof(wplcmt)};
WINDOWPLACEMENT wplcmt = {sizeof(wplcmt), 0, SW_SHOW, {0, 0}, {0, 0}, {0, 0, 0, 0}};

事实上,在这种情况下,我甚至不确定所有的 C++ 编译器都支持复合字面初始化。此外,如果成员更改顺序或类型,而您的值仍适合,则不会出现错误。
个人而言,我选择在能够使用 C99 的情况下声明给定结构体,一次性将所有已知值提前列出,例如:
WINDOWPLACEMENT const wplcmt = {.length = sizeof(wplcmt), .showCmd = SW_SHOW};

更新0

看来我所提到的“全部初始化”仅适用于数组?我的错误,这使得C ++略微更易用。


2
WINDOWPLACEMENT wplcmt = {sizeof(wplcmt)}; // 所有成员都被设置为 sizeof(wplcmt) 其他的值不是被设置为0吗? - Coder
“在G++中”指的是“在GNU C++中”,即“使用g++支持的编译器扩展”,对吗? - Karl Knechtel
1
漂亮的双精度声明!这是什么意思? - Johannes Schaub - litb
@Karl Knechtel:是的,我使用 -std=gnu++* - Matt Joiner

0
如果您使用Visual Studio,我强烈建议您使用:
WINDOWPLACEMENT wplcmt;
SecureZeroMemory((LPVOID)&wplcmt, sizeof(WINDOWPLACEMENT));
wplcmt.length = sizeof(WINDOWPLACEMENT);

因为优化编译器可以完全从生成的代码中删除memset调用,如果您确实需要将这些值设置为零,则会引起很多麻烦。

SecureZeroMemory永远不会被删除。


0

使用memset应该具有更好的性能,因为通常它是用高度优化的汇编语言编写的。


大多数情况下,如果结构体足够大,memset 将被发出,至少在 MSVC 中是这样。 - Coder

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接