为什么需要告诉结构物大小?

8
我注意到在c/c++中,很多Win32 API结构需要告诉它们的大小。 例如:someStruct.cbFormat = sizeof(SomeStruct) 为什么会这样?这只是因为历史原因吗?还有,你知道"pb"代表什么吗?
编辑:哦,是的,我是指“cbFormat”。
5个回答

12

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

想象以下声明:

struct WinData
{
   long flags;
}
BOOL GetWinData(WinData * wd);
你正在这样调用它:
WinData wd;
GetWinData(&wd);

未来的操作系统版本可能会扩展此功能以支持更多内容

struct WinData
{
   long flags;
   long extraData;
}

然而,如果你是针对“较旧”的SDK编译的,GetWinData就没有机会发现你不知道extraData。如果它无论如何都填充,它将覆盖堆栈上的数据。BOOOM!

这就是为什么在结构体中添加了“由调用者指定大小”的内容,并在末尾附加了新成员。 GetWinData实现可以检查大小并决定“这个可怜的家伙还不知道所有的新功能”。


2
应该有一个专门的网站,致力于列举人们认为Windows糟糕的原因,并提供合理的解释(就像这篇文章一样),解释为什么某些事情会以某种方式完成。有趣的是,向后兼容性经常被用作Windows中某些奇怪行为的解释 - 我有时会用它来让Mac粉丝闭嘴,提醒他们每次更新操作系统时所有软件都停止工作。 - MusiGenesis
有时他们使用大小或版本变量,有时他们使用 somestructEx 表示额外或扩展。 - Gregor Brandt
11
我相信有这样一个网站,它叫做雷蒙德·陈的博客。 :-) - James McNellis
与那个“Peterc Hen”的家伙有什么关系吗? - MusiGenesis
我在那里真的获得了我的5秒钟的名气 :) - (向下滚动到Nish的评论) - peterchen

8

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

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

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

此前,STARTUPINFOEX 结构已被扩展,它包含相同的第一部分但不同的尺寸。根据 cb 的值,Windows 将知道是否查看新的 lpAttributeList 字段。


2
其他主要操作系统是否也采用这种API版本控制方式,或者有更好的替代方案? - MusiGenesis

1

这个问题是匈牙利命名法的一个例子,基本上是一种将变量类型编码到其名称中的方案。


我相信这个问题并不是针对命名规范的。 - YeenFei
虽然这是匈牙利命名法主要问题的一个好的附带说明:如果你开始依赖那些小字母来真正表示重要的东西,你就会遇到麻烦。 - MusiGenesis
实际上,这两个字符“cb”表示的是您的IDE不会显示的字节数。 - CMircea

1

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

C是一种过程式语言,对于结构体的操作更加有限。


0

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

// 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
@MSalters - 不幸的是,它可能会失败。考虑从 struct foo { int size; short s1; char c1; } 开始,然后将其扩展为 struct foo { int size; short s1; char c1; char c2; }。两者的大小都为8,因此您无法确定调用方是否具有带有或不带有字段 c2 的定义。 - R Samuel Klatchko
根据Win32的规定,这不是定义struct foo第二个版本的有效方式。第一个DWORD必须设置为sizeof(object),在v1和v2之间必须有所不同。 - MSalters

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