【问题标题】:Is it preferred/okay to use structure-initialization ({...}) over memset, etc.?使用结构初始化({...})而不是 memset 等是首选/可以吗?
【发布时间】:2010-12-12 23:12:40
【问题描述】:

代码:

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。

那么使用这样的语法是好还是坏? 使用此类初始化是一种好习惯吗?

【问题讨论】:

  • 这听起来像是一个拖钓问题。
  • IMO 都可以,但为什么 Windows(或其他任何东西)会 memcmp 结构?首先,对具有未使用位的结构执行 memcmp 肯定是不明智的。不要求那些未使用的填充字节为零(或任何其他值),因此任何考虑它们的比较都是无效的。有没有什么真正做到这一点,或者您是否担心“以防万一”?
  • 一旦超出您的代码范围,您就无法确定接下来会发生什么。所以我只是想知道如何编写更干净、更健壮的代码。

标签: c++ c windows initialization


【解决方案1】:

您显示的第二个代码,

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

太可怕了。混淆、低效、冗长,你把这一切都塞进去了。

第一个代码sn -p,

WINDOWPLACEMENT wplcmt = {sizeof(WINDOWPLACEMENT)};

是,除了混淆,首选方式,除非你想

  • 花更多时间不必要地编写更多代码,

  • 让读者花更多时间阅读和不必要地分析您的冗长代码,

  • 执行效率降低,并且

  • 提供错误入口。

顺便问一下,您使用的混淆名称wplcmt 是怎么回事?

你为什么要混淆名字?

您的问题是真实的还是只是拖钓?

干杯,

EDIT:问题已被编辑。以上问题是对原始标题/问题的回应,“这种结构分配有多邪恶?”。我将按原样保留我的答案,以便为 cmets 提供上下文。

EDIT 2:上下文发生了更大的变化:OP 的昵称从“Madman”变为“Coder”。因此,虽然最初是“Madman”的“How eveil is”普通代码,但现在是“Coder”的“Is it prefer...”。哦,好吧,我的意思是,我不会在评论中称他为“疯子”,就像现在出现的那样;这是他自称的,当时他的昵称。

【讨论】:

  • 我更喜欢单行方法,但正如我所提到的,填充和 memcmp 会破坏。假设我有 struct {int32_t v1, int16_t v2 /*nonexisting padding int16_t*/},我使用 = {} 进行初始化,但我调用的库使用 ZeroMemory 或 memset 初始化相同结构的另一个副本并执行 memcmp。我的结构中的填充值是未定义的,对,即使实际成员变量相等,也可以返回结构不同。抱歉混淆了,这只是一个假设的代码,我倾向于在任何地方使用可读性好的名称。
  • 你的回答很好(事实上),但是你不应该无缘无故地指责别人。因此,您的回答不会赢得我的投票...
  • @Madman:如果可以,请避免检查填充字节是否为零的库。或表现出其他未定义行为。 :-)
  • @jpalecek:我不是在指责,只是在问,这不是无缘无故的,这是出于上述原因。考虑:一个“疯子”,他展示了类似微软的低级 C 代码,标记为 C++,并询问比较正常的代码有多么“邪恶”,传达出这样的印象:它再好不过了。对我来说这听起来像拖钓
  • C/C++ 是因为尽管我尽可能多地编写 C++,但没有人将 WINAPI 转换为 C++,至少 AFAIK。因此,如果有人知道一个干净的 C++ 解决方案,那么欢迎。关于更正常的问题,到目前为止,我们有 2 个完全相反的答案,所以即使我也喜欢你所捍卫的解决方案,但我还不相信它是好的。
【解决方案2】:

使用 memset。然后每个人都会立即看到代码的作用,并且不太可能产生任何意想不到的副作用。

【讨论】:

  • +1。我同意。尤其是因为这是用于初始化原生 Windows 结构的长期习惯用法。
  • ... 如果我们关心的是“惯用的”和本地 Windows 特定的,不应该使用 ZeroMemory() 而不是 memset()? :)
  • 如果结构有一个指针类型并且指针不是所有位为零,或者浮点值也可能不是所有位为零,IIRC?
  • 然后我们还明确地设置了这些值。给定 Windows 类的文档将告诉我们应该如何初始化它。
【解决方案3】:

我经常与这种初始化作斗争。在 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++ 更实用。

【讨论】:

  • "WINDOWPLACEMENT wplcmt = {sizeof(wplcmt)}; //所有成员都设置为sizeof(wplcmt)" 其他的值不都是0吗?
  • “在 G++ 中”是指“在 GNU C++ 中”,即“带有 g++ 支持的编译器扩展”,是吗?
  • 很好的双重声明!它是什么意思?
  • @Karl Knechtel:是的,我愿意。 -std=gnu++*
【解决方案4】:

memset 应该有更好的性能,因为它通常是用高度优化的 asm 编写的

【讨论】:

  • memset 大部分时间都是emmited,如果结构足够大,至少使用MSVC
【解决方案5】:

如果您使用 Visual Studio,我强烈建议您使用:

WINDOWPLACEMENT wplcmt;
SecureZeroMemory((LPVOID)&wplcmt, sizeof(WINDOWPLACEMENT));
wplcmt.length = sizeof(WINDOWPLACEMENT);

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

SecureZeroMemory 永远不会被删除。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-03-24
    • 2018-05-14
    • 1970-01-01
    • 2011-01-01
    • 1970-01-01
    • 2020-12-07
    • 2020-11-13
    相关资源
    最近更新 更多