为什么在结构中放入struct的大小?

时间:2014-09-18 07:02:15

标签: c api

我第一次开始在Windows上使用SetupAPI,并注意到我总是需要在调用填充结构的函数之前将结构的大小放入结构中。例如:

SP_DEVICE_INTERFACE_DATA dintf_data;
dintf_data.cbSize = sizeof(dintf_data);
SetupDiEnumDeviceInterfaces(di, NULL, &GUID_DEVINTERFACE_USB_DEVICE, 0, &dintf_data);

这样做的原因是什么?

这是实现他们想要实现的目标的好方法,还是有更好的方法?

(我能想到的唯一原因是它是处理API版本控制的一种方式。)

2 个答案:

答案 0 :(得分:5)

是的,你知道了,API版本。

操作系统检查传入的大小以确定您正在使用的结构版本。随着为较新版本的API添加功能,添加字段(从未删除),以增加字段大小,因此操作系统可以很容易地知道您正在使用的版本。

答案 1 :(得分:2)

这是一种版本控制方式,可确保向后(和向前!)兼容性。

想象一下,你有一些消息框struct

struct MessageBoxData {
    const char *message;
    const char *caption;
};

您可以将其编译到API中,然后使用它来显示包含messagecaption的消息框。你很好。

稍后,您将发布API版本2,它会为显示的图标添加另一个字段:

struct MessageBoxData {
    const char *message;
    const char *caption;
    unsigned char icon;
};

让我们假设编译器没有神奇地填充或重构元素的顺序,并且任何程序都可以将这个更复杂的API版本2结构传递给API版本1库而不会出现问题。

但是,反过来会发生什么?使用针对API版本1编译的程序和API版本2的库。

图书馆会尝试阅读icon,这会导致在此struct的有效边界之外进行阅读!

以类似方式,如果您尝试使用memcpy()复制内容,则可能会出现问题。

为了避免这种情况,Windows API通常希望第一个成员定义结构的实际大小。在这个例子中,你可以创建这样的东西:

// Version 1
struct MessageBoxData {
    unsigned int size;
    const char *message;
    const char *caption;
};

// Version 2
struct MessageBoxData {
    unsigned int size;
    const char *message;
    const char *caption;
    unsigned char icon;
};

您可能认为他们可以在被调用的函数中使用sizeof()。虽然这在理论上是正确的,但它不能解决编译时间(库和程序)之间的不同定义问题。