您对“为什么不使用大小为 1 的数组”的直觉是正确的。
代码做错了“C struct hack”,因为零长度数组的声明违反了约束。这意味着编译器可以在编译时立即拒绝您的 hack,并带有停止翻译的诊断消息。
如果我们想进行黑客攻击,我们必须偷偷溜过编译器。
进行“C struct hack”(与可追溯到 1989 年的 ANSI C 并且可能更早的 C 方言兼容)的正确方法是使用大小为 1 的完全有效的数组:
struct someData
{
int nData;
unsigned char byData[1];
}
此外,byData 之前部分的大小不是sizeof struct someData,而是使用以下公式计算的:
offsetof(struct someData, byData);
要在byData 中分配一个具有42 字节空间的struct someData,我们将使用:
struct someData *psd = (struct someData *) malloc(offsetof(struct someData, byData) + 42);
请注意,即使在数组大小为零的情况下,这个offsetof 计算实际上也是正确的计算。你看,sizeof 整个结构可以包含填充。例如,如果我们有这样的事情:
struct hack {
unsigned long ul;
char c;
char foo[0]; /* assuming our compiler accepts this nonsense */
};
struct hack 的大小很可能由于 ul 成员而被填充以对齐。如果unsigned long 是四个字节宽,那么sizeof (struct hack) 很可能是 8,而offsetof(struct hack, foo) 几乎可以肯定是 5。offsetof 方法是在数组。
这将是重构代码的方法:使其符合经典的、高度可移植的 struct hack。
为什么不使用指针?因为指针占用了额外的空间并且必须被初始化。
不使用指针还有其他充分的理由,即指针需要地址空间才能有意义。 struct hack 是可外部化的:也就是说,在某些情况下,这种布局符合外部存储,例如文件、数据包或共享内存的区域,在这种情况下,您不需要指针,因为它们没有意义。
几年前,我在内核和用户空间之间的共享内存消息传递接口中使用了 struct hack。我不想要那里的指针,因为它们只对生成消息的进程的原始地址空间有意义。该软件的内核部分使用自己在不同地址的映射来查看内存,因此一切都基于偏移计算。