数组零长度

时间:2008-11-17 06:54:24

标签: c++ arrays visual-c++ flexible-array-member

我正在重构一些旧代码,并且发现了一些包含零长度数组的结构(下图)。当然,由pragma压制的警告,但我没有通过包含这种结构的“新”结构创建(错误2233)。数组'byData'用作指针,但为什么不使用指针呢?或长度为1的数组?当然,没有添加评论让我享受这个过程...... 有什么理由使用这样的东西?重构那些的任何建议吗?

struct someData
{
   int nData;
   BYTE byData[0];
}

NB它是C ++,Windows XP,VS 2003

5 个答案:

答案 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 someDatabyData之前的部分大小使用以下公式计算:

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()表达式意外搞乱。