我正在重构一些旧代码,并且发现了一些包含零长度数组的结构(下图)。当然,由pragma压制的警告,但我没有通过包含这种结构的“新”结构创建(错误2233)。数组'byData'用作指针,但为什么不使用指针呢?或长度为1的数组?当然,没有添加评论让我享受这个过程...... 有什么理由使用这样的东西?重构那些的任何建议吗?
struct someData
{
int nData;
BYTE byData[0];
}
NB它是C ++,Windows XP,VS 2003
答案 0 :(得分:34)
是的,这是一个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的对象,其中包含一个指定长度的数组。
答案 1 :(得分:24)
不幸的是,有几个原因可以在结构的末尾声明零长度数组。它本质上使您能够从API返回可变长度结构。
Raymond Chen撰写了一篇关于这个主题的优秀博客文章。我建议你看一下这篇文章,因为它可能包含你想要的答案。
请注意,在他的帖子中,它处理的是大小为1而不是0的数组。这是因为零长度数组是最新的标准入口。他的帖子应该仍然适用于你的问题
http://blogs.msdn.com/oldnewthing/archive/2004/08/26/220873.aspx
修改强>
注意:尽管Raymond的帖子说0长度数组在C99中是合法的,但事实上它们在C99中仍然不合法。这里你应该使用长度为1的数组
而不是0长度数组答案 2 :(得分:22)
这是一个旧的C hack,允许使用灵活大小的阵列。
在C99标准中,这不是必需的,因为它支持arr []语法。
答案 3 :(得分:8)
你的直觉"为什么不使用1号和34号的数组;是现场。
代码正在执行" 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的前一部分。
这将是重构代码的方法:使其符合经典,高度可移植的struct hack。
为什么不使用指针?因为指针占用额外的空间并且必须初始化。
还有其他充分的理由不使用指针,即指针需要地址空间才能有意义。 struct hack是可外部化的:也就是说,在某些情况下,这样的布局符合外部存储,例如文件,数据包或共享内存区域,在这些区域中你不需要指针,因为它们没有意义。
几年前,我在内核和用户空间之间传递接口的共享内存消息中使用了struct hack。我不想在那里指针,因为它们只对生成消息的进程的原始地址空间有意义。软件的内核部分使用自己在不同地址的映射来查看内存,因此所有内容都基于偏移计算。
答案 4 :(得分:1)
值得指出IMO进行大小计算的最佳方法,该方法在上面链接的Raymond Chen文章中使用。
struct foo
{
size_t count;
int data[1];
}
size_t foo_size_from_count(size_t count)
{
return offsetof(foo, data[count]);
}
第一个条目在所需分配结束时的偏移量也是所需分配的大小。 IMO是一种非常优雅的大小计算方法。变量大小数组的元素类型是什么并不重要。 offsetof(或Windows中的FIELD_OFFSET或UFIELD_OFFSET)始终以相同的方式编写。没有sizeof()表达式意外搞乱。