零长度的数组
我正在重构一些旧的代码,并发现几个包含零长度数组的结构(见下文)。 警告当然是压制杂注,但是我没有通过包含这种结构的“新”结构来创build(错误2233)。 数组'byData'用作指针,但是为什么不使用指针呢? 或长度为1的数组? 当然,没有任何意见可以让我享受这个过程…任何原因使用这样的事情? 任何意见重构这些?
struct someData { int nData; BYTE byData[0]; }
注意它是C ++,Windows XP,VS 2003
是的,这是一个C-Hack。
要创build任何长度的数组:
struct someData* mallocSomeData(int size) { struct someData* result = (struct someData*)malloc(sizeof(struct someData) + size * sizeof(BYTE)); if (result) { result->nData = size; } return result; }
现在你有一个具有指定长度数组的someData对象。
不幸的是,有几个原因让你在结构的末尾声明一个零长度的数组。 它本质上使您能够从API返回一个可变长度结构。
Raymond Chen在这个问题上做了一个很好的博客文章。 我build议你看看这个post,因为它可能包含你想要的答案。
在他的post中注意到,它处理的是大小为1而不是为0的数组。 这是因为零长度数组是更近期进入标准的情况。 他的post应该仍然适用于你的问题。
http://blogs.msdn.com/oldnewthing/archive/2004/08/26/220873.aspx
编辑
注意:即使Raymond的post说在C99中长度为0的数组是合法的,但事实上在C99中它仍然不合法。 应该使用长度为1的数组而不是0长度的数组
这是一个古老的C hack允许灵活大小的数组。
在C99标准中,这不是必须的,因为它支持arr []语法。
关于“为什么不使用一个大小为1的数组”的说法是现货。
代码是做“C结构黑客”错误,因为零长度数组的声明是违反约束。 这意味着编译器可以在编译时立即拒绝你的黑客攻击,并带有停止翻译的诊断消息。
如果我们要进行破解,我们必须偷偷地通过编译器。
正确的做“C struct hack”(与C语言兼容的方法可以追溯到1989年的ANSI C,可能更早)是使用一个完全有效的数组1:
struct someData { int nData; unsigned char byData[1]; }
而且,不是sizeof struct someData
,而是使用以下内容计算byData
之前的部分的大小:
offsetof(struct someData, byData);
要在struct someData
分配一个空间为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 */ };
由于ul
成员, struct hack
的大小很可能被填充以便alignment。 如果unsigned long
是四个字节宽,那么很有可能sizeof (struct hack)
是8,而offsetof(struct hack, foo)
几乎肯定是5. offsetof
方法是获得结构的前一部分的精确大小的方式就在arrays之前。
所以这将是重构代码的方法:使其符合经典,高度可移植的struct hack。
为什么不使用指针? 因为指针占用额外的空间并且必须被初始化。
还有其他很好的理由不使用指针,即指针需要一个地址空间才能有意义。 struct hack是可以外部化的:也就是说,在某些情况下,这样的布局符合外部存储,如文件,数据包或共享内存区域,其中不需要指针,因为它们没有意义。
几年前,我在内核和用户空间之间的共享内存消息传递接口中使用了struct hack。 我不想在那里指针,因为它们只对生成消息的进程的原始地址空间有意义。 软件的内核部分在内存中使用自己的映射在不同的地址,所以一切都基于偏移计算。