我有时在代码中看到这个:
struct S
{
int count; // length of array in data
int data[1];
};
Run Code Online (Sandbox Code Playgroud)
S的存储分配大于sizeof(S)数据可以为其数组提供更多空间.它然后用作:
S *s;
// allocation
s->data[3] = 1337;
Run Code Online (Sandbox Code Playgroud)
我的问题是,为什么data不是指针?为什么长度为1的数组?
如果您声明data为指针,则必须为data数组分配一个单独的内存块,即您必须进行两次分配而不是一次.虽然实际功能没有太大差异,但仍可能会对性能产生负面影响.它可能会增加内存碎片.这可能导致结构存储器被"远离" data数组存储器分配,导致数据结构的缓存行为不良.如果您使用自己的内存管理例程(如池化分配器),则必须设置两个分配器:一个用于结构,一个用于数组.
通过使用上述技术(称为"struct hack"),您可以data在一个块中为整个结构(包括数组)分配内存,只需调用一次malloc(或自己的分配器).这就是它的用途.除此之外,它确保结构存储器尽可能靠近数组存储器(即它只是一个连续的块),因此数据结构的缓存行为是最佳的.
Raymond Chen写了一篇很好的文章,关于为什么变长结构选择这种模式而不是其他许多模式(包括指针).
他没有直接评论为什么在数组上选择指针,但Steve Dispensa在评论部分提供了一些见解.
来自史蒂夫
typedef struct _TOKEN_GROUPS {
DWORD GroupCount;
SID_AND_ATTRIBUTES *Groups;
} TOKEN_GROUPS, *PTOKEN_GROUPS;
Run Code Online (Sandbox Code Playgroud)
这仍然会强制组指针对齐,但是当你想到参数编组时它就不太方便了.
在驱动程序开发中,开发人员有时会遇到通过METHOD_BUFFERED IOCTL从用户模式向内核模式发送参数.像这样的嵌入式指针的结构代表了从等待发生的安全缺陷到简单的PITA的任何事物.