您的位置:首页 > 其它

为什么一些结构中尾部数组的大小是1?

2006-04-11 20:46 281 查看
一些Windows结构都是可变大小的,一个固定大小的头之后是一个可变大小的数组。当这些结构被声名的时候,数组大小都声明为1,举个例子:

typedef struct _TOKEN_GROUPS {

DWORD GroupCount;

SID_AND_ATTRIBUTES Groups[ANYSIZE_ARRAY];

} TOKEN_GROUPS, *PTOKEN_GROUPS;

如果你看这个头文件,你将看到这个ANYSIZE_ARRAY被定义为1,因此这个结构中声明了一个动态扩展大小的数组。

使用这个声明,你会像下面这个代码一样对一个可变大小的TOKEN_GROUPS结构进行内存分配:

PTOKEN_GROUPS TokenGroups =

malloc(FIELD_OFFSET(TOKEN_GROUPS, Groups[NumberOfGroups]));

初始化化数组:

TokenGroups->GroupCount = NumberOfGroups;

for (DWORD Index = 0; Index = NumberOfGroups; Index++) {

TokenGroups->Groups[Index] = ...;

}

很多人认为这个结构应该设计成下面这个样子:

typedef struct _TOKEN_GROUPS {

DWORD GroupCount;

} TOKEN_GROUPS, *PTOKEN_GROUPS;

(在文中, 以斜体书写的代码都是错误的或是假设的)

分配内存的代码变成了:

PTOKEN_GROUPS TokenGroups =

malloc(sizeof(TOKEN_GROUPS) +

NumberOfGroups * sizeof(SID_AND_ATTRIBUTES));

这个方法有两个缺点,一个是次要的,而另外一个是致命的。

首先,次要的缺点是:这种做法非常难以存取可变大小数据。初始化TOKEN_GROUPS代码变成:

TokenGroups->GroupCount = NumberOfGroups;

for (DWORD Index = 0; Index = NumberOfGroups; Index++) {

((SID_AND_ATTRIBUTES *)(TokenGroups + 1))[Index] = ...;

}

真正的缺点是致命的。上述的代码会在64位Windows上crash。SID_AND_ATTRIBUTES结构如下:

typedef struct _SID_AND_ATTRIBUTES {

PSID Sid;

DWORD Attributes;

} SID_AND_ATTRIBUTES, * PSID_AND_ATTRIBUTES;

通过观察,结构中第一个成员变量是一个指针,PSID。SID_AND_ATTRIBUTES结构需要指针对齐,在64位Windows是8位对齐。也就是说,被设想的TOKEN_GROUPS  结构是一个DWORD,因此只需要4位对齐。sizeof(TOKEN_GROUPS)是4。

我相信你已经找到问题的所在了。

在被设想的结构定义中,SID_AND_ATTRIBUTES数组将不是8个字节对齐,而只是4个字节对齐。没有在GroupCount 和第一个SID_AND_ATTRIBUTES之间增加padding。在读取结构中数组的时候会导致STATUS_DATATYPE_MISALIGNMENT异常。

也许你会说,为什么不用0长度的数据来代替1呢?

因为从1999起在C标准中0长度的数组将不再合法。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐