为什么一些结构中尾部数组的大小是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长度的数组将不再合法。
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长度的数组将不再合法。
相关文章推荐
- ES6一些有意思的新语法-结构数组
- 为什么定义数组时要定义好数组大小
- 结构体内数组大小不定
- 当数组是函数的参数时, 为什么sizeof 不能正确报告数组的大小?
- 为什么用const说明的常量不能用来定义一个数组的初始大小?
- 一个有关计算结构体数组大小的问题
- BFS(广度优先搜索)这里用来两种方法存储路径,1.同map大小的数组来存储路径2.通过生成反向树状结构存储路径
- 妙用0元素数组 实现大小可变结构体
- 用数组结构实现大小固定的栈和队列
- c语言 数组名&&结构体名 为什么不能用数组名给数组赋值,结构体却可以
- 使用数组实现固定大小的栈结构
- 简单问题:sizeof(char型数组)为数组大小,而不是4, 为什么? 100!
- 给定一个整数数组,1≤a [i]≤n(n =数组的大小),一些元素出现两次,其他出现一次 查找在该数组中出现两次的所有元素
- 简单问题:sizeof(char型数组)为数组大小,而不是4, 为什么? 100!
- 程序猿之---C语言细节29(#define宏大小、空结构体大小、柔性数组不知道你见过没)
- 简单的结构数组 先预占一些位置吧
- sas数组,数组的语法与一些特殊定义,获取维度大小
- 结构体零长数组的妙用及warning C4200:使用了非标准扩展:结构/联合中的零大小数组解决
- 学习c语言遇到的一些问题(4)(Linux)(不确定大小的数组)
- 妙用0元素数组 实现大小可变结构体