我正在重构一些旧的代码,并发现很少的结构包含零长度数组(如下).当然,由pragma压制的警告,但我没有通过包含这种结构的"新"结构创建(错误2233).数组'byData'用作指针,但为什么不使用指针呢?或长度为1的数组?当然,没有添加任何评论让我喜欢这个过程...任何使用这种东西的原因?重构那些的任何建议?
struct someData { int nData; BYTE byData[0]; }
NB它是C++,Windows XP,VS 2003
是的,这是一个C-Hack.
要创建任何长度的数组:
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在这个主题上写了一篇很棒的博文.我建议你看看这篇文章,因为它可能包含你想要的答案.
请注意,在他的帖子中,它处理的是大小为1而不是0的数组.这是因为零长度数组是更新的标准入口. 他的帖子应该仍适用于你的问题.
http://blogs.msdn.com/oldnewthing/archive/2004/08/26/220873.aspx
编辑
注意:尽管Raymond的帖子说0长度数组在C99中是合法的,但事实上它们在C99中仍然不合法.这里应该使用长度为1的数组,而不是0长度数组
这是一个旧的C hack,允许灵活大小的阵列.
在C99标准中,这不是必需的,因为它支持arr []语法.
你对"为什么不使用1号阵列"的直觉就是现场.
代码执行"C struct hack"错误,因为零长度数组的声明是违反约束的.这意味着编译器可以在编译时立即拒绝您的黑客攻击,并使用停止转换的诊断消息.
如果我们想要进行黑客攻击,我们必须将它偷偷摸摸地通过编译器.
执行"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字节分配空间byData
,我们将使用:
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.我不想在那里使用指针,因为它们只对生成消息的进程的原始地址空间有意义.软件的内核部分使用自己在不同地址的映射来查看内存,因此所有内容都基于偏移计算.