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

一些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长度的数组将不再合法。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值