【问题标题】:Why do structures need to be told how big they are?为什么需要告诉结构它们有多大?
【发布时间】:2010-05-31 01:11:22
【问题描述】:

我注意到在 c/c++ 中需要告诉许多 Win32 API 结构它们有多大。 即someStruct.pbFormat = sizeof(SomeStruct)

为什么会这样?仅仅是因为遗产的原因吗?也知道“pb”代表什么吗?

编辑:哎呀,是的,我的意思是“cbFormat”

【问题讨论】:

    标签: c++ c struct sizeof


    【解决方案1】:

    这是为了在扩展 Windows API 时向后兼容。

    想象以下声明

    struct WinData
    {
       long flags;
    }
    BOOL GetWinData(WinData * wd);
    

    你这样称呼它:

    WinData wd;
    GetWinData(&wd);
    

    未来的操作系统版本可能会将其扩展到

    struct WinData
    {
       long flags;
       long extraData;
    }
    

    但是,如果您针对“较旧”的 SDK 进行编译,GetWinData 没有机会发现您不了解 extraData。如果它无论如何都会填充它,它将覆盖堆栈上的数据。轰!

    这就是为什么将“调用者已知的大小”添加到结构中,并在末尾附加新成员的原因。 GetWinDataimplementation 可以检查大小并决定“这个可怜的家伙还不知道所有新功能”。

    【讨论】:

    • +1 应该有一个网站专门用于说明人们认为 Windows 很糟糕的原因,以及对为什么以某种方式完成某事的理性、明智的解释(例如这个)。有趣的是,向后兼容性如何经常解释 Windows 中一些奇怪的东西 - 我有时用它来让 Mac 粉丝安静下来,提醒他们所有的软件如何停止与每个新操作系统一起工作。
    • 有时他们使用大小或版本变量,有时他们使用 somestructEx 来表示额外或扩展。
    • @MusiGenesis:我相信有这样一个网站,它叫Raymond Chen's Blog。 :-)
    • 与这个“Peterc Hen”家伙有任何关系吗?
    • 我实际上在那里获得了 5 秒的 sh/fame :) - blogs.msdn.com/b/oldnewthing/archive/2006/04/12/574928.aspx(向下滚动到 Nish 的评论)
    【解决方案2】:

    这样可以在 API 的未来版本中扩展结构,然后 Windows 可以知道(通过调用者传入的大小)应该查看或不查看哪些字段。它基本上是 API 版本控制的一种粗略形式。

    通常这些计数字节以cb 为前缀,代表“字节计数”。例如,STARTUPINFO 结构以:

    开头
    typedef struct _STARTUPINFO {
      DWORD  cb;
      LPTSTR lpReserved;
      ...
    } STARTUPINFO, *LPSTARTUPINFO;
    

    这在某个时候使用STARTUPINFOEX 结构进行了扩展,该结构包含相同的第一部分但大小不同。根据cb 的值,Windows 将知道是否查看新的lpAttributeList 字段。

    【讨论】:

    • 其他主要操作系统是否以这种方式进行 API 版本控制,或者是否有更好的替代方案在使用?
    【解决方案3】:

    pb 是Hungarian Notation 的一个示例,基本上是一种将变量类型编码为其名称的方案。

    【讨论】:

    • 我相信这个问题不针对命名约定
    • 虽然这是匈牙利符号的主要问题的一个很好的附带说明:如果你开始指望这些小字母实际上意味着重要的东西,你就有麻烦了。
    • 实际上两个字符 'cb' 表示你的 IDE 不会:字节数。
    【解决方案4】:

    因为 Win32 API 是 C API - 不是 C++,所以扩展 API 的面向对象的方法不可用。使用 C++ API 的新功能将使用从旧结构派生的结构并调用采用基本结构的接口,从而确保类型安全。

    C 是一种过程语言,并且在您可以对结构执行的操作方面受到更多限制。

    【讨论】:

      【解决方案5】:

      这样未来的版本可以添加额外的字段并且仍然能够提供向后二进制兼容性:

      // CAUTION - the code is not 100% accurate and can fail due to packing rules.
      // For illustrative purposes only
      if (offsetof(struct foo, field) > f->pbFormat)
      {
         // called with a version that predates the addition of
         // field so revert to a default value
      }
      

      【讨论】:

      • 实际上它不会因为打包规则而失败,因为Win32隐含了这些规则。
      • @MSalters - 不幸的是,它可能会失败。考虑从 struct foo { int size; short s1; char c1; } 开始,然后将其扩展到 struct foo { int size; short s1; char c1; char c2; } 两者的大小都为 8,因此您将无法判断调用者是否具有带或不带字段 c2 的定义。
      • 根据 Win32 规则,这不是定义 struct foo 版本 2 的有效方法。第一个 DWORD,设置为 sizeof(object),在 v1 和 v2 之间必须不同。
      猜你喜欢
      • 2010-12-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-01-15
      • 1970-01-01
      • 2011-04-23
      • 1970-01-01
      相关资源
      最近更新 更多