为什么有些结构体中会定义一个字节的数组

为什么有些结构体中会定义一个字节的数组

作者:BlogUpdater |  时间:2021-09-23 |  浏览:127 |  评论已关闭 条评论

有一些Windows结构体是可变长度的,它们通常会有一个固定长度的头部,然后接下来是一个可变长度的数组。当这些结构体被声明的时候,它们通常会被声明为一个只有一个字节元素的数组,如下图所示:

如果我们观察一下头文件中的定义,就会发现,这里的ANYSIZE_ARRAY被定义成了1,也就是说,这个结构体的结尾是一个只包含1个字节的数组。

通过这样的声明,我们可以分配一个可变长度的结构体,如下图所示:

然后,我们可以通过下图所示的方式来对结构体进行初始化:

有一些开发者可能会认为这个结构体应该像下图这样定义:

然后,结构体的内存分配就看起来像下图这样进行:

这样的做法,会有两个缺陷:一个表面上可见的缺陷,和一个致命缺陷,容我慢慢道来。

首先,这样的设计,会导致客户非常难以访问一个可变长度的数据,对TOKEN_GROUPS结构体的初始化可能如下图这样进行:

真正的缺陷在于,上述代码会在一台64位Windows系统上崩溃,下面是SID_AND_ATTRIBUTES结构体的定义:

上述的结构体定义中,第一个成员是一个PSID指针。SID_AND_ATTRIBUTES结构体需要指针对齐,而在一台64位的Windows系统上是8字节对齐的。另一方面,TOKEN_GROUPS结构仅仅包含一个DWORD,因此它仅需要在4字节边界上对齐,而sizeof(TOKEN_GROUPS)的值为4。

我希望你能看出来现在是什么样一个状况。

从底层结构上来看,SID_AND_ATTRIBUTES结构不会对齐到一个8字节的边界,而是会对齐到4个字节边界,而用来填充这中间的间隙的部分被忽视了。所以,当尝试访问这个数组的成员的时候,会直接导致一个STATUS_DATATYPE_MISALIGNMENT异常。

你可能会问了,那为什么不使用一个长度为0的数组,而使用1个字节的数组呢?

因为:”时间旅行目前还只是一个幻想而已。”

在1999年之前,标准C语言还不支持一个长度为0的数组。而Windows的开发远远早于这个时间点,因而不大可能会利用这个语言特性。

总结
可变长度的数据结构,在网络数据传输中颇有用处。
我为啥知道?兄弟我吃过这方面的亏。

最后
Raymond Chen的《The Old New Thing》是我非常喜欢的博客之一,里面有很多关于Windows的小知识,对于广大Windows平台开发者来说,确实十分有帮助。
本文来自:《Why do some structures end with an array of size 1?》

最近我写了个东西
正如你们所知道的,拓扑梅尔智慧办公平台(Topomel Box)是一款绿色软件,主要面向经常使用电脑的朋友。它提供了各种提升办公效率的小功能,同时操作上尽可能地简单方便。
我想:你值得拥有。

评论已关闭。