灵活的数组成员在C ++中是否有效?

时间:2010-12-10 19:55:32

标签: c++ flexible-array-member

在C99中,您可以声明结构的灵活数组成员:

struct blah
{
    int foo[];
};

然而,当有人在这里工作时尝试使用C ++中的clang编译一些代码时,该语法不起作用。 (它一直在与MSVC合作。)我们必须将其转换为:

struct blah
{
    int foo[0];
};

通过C ++标准,我发现根本没有提到灵活的成员数组;我一直认为[0]是一个无效的声明,但显然对于一个灵活的成员数组它是有效的。灵活的成员数组在C ++中实际上是否有效?如果是,请使用正确的声明[][0]

10 个答案:

答案 0 :(得分:21)

C ++最初是在1998年标准化的,所以它早于将灵活的数组成员添加到C(这在C99中是新的)。 2003年有一个C ++的更正,但没有添加任何相关的新功能。 C ++的下一个版本(C ++ 0x)仍在开发中,似乎没有添加灵活的数组成员。

答案 1 :(得分:18)

C ++在结构末尾不支持C99灵活数组成员,使用空索引表示法或0索引表示法(禁止特定于供应商的扩展):

struct blah
{
    int count;
    int foo[];  // not valid C++
};

struct blah
{
    int count;
    int foo[0]; // also not valid C++
};

据我所知,C ++ 0x也不会添加它。

但是,如果将数组的大小设置为1个元素:

struct blah
{
    int count;
    int foo[1];
};
事情是有效的,并且工作得很好。您可以使用不太可能出现一个错误的表达式来分配适当的内存:

struct blah* p = (struct blah*) malloc( offsetof(struct blah, foo[desired_number_of_elements]);
if (p) {
    p->count = desired_number_of_elements;

    // initialize your p->foo[] array however appropriate - it has `count`
    // elements (indexable from 0 to count-1)
}

因此它可以在C90,C99和C ++之间移植,并且与C99的灵活阵列成员一样好。

Raymond Chen写了一篇很好的文章:Why do some structures end with an array of size 1?

注意:在Raymond Chen的文章中,在一个例子中有一个错误/错误来初始化'灵活的'阵列。它应该是:

for (DWORD Index = 0; Index < NumberOfGroups; Index++) { // note: used '<' , not '='
  TokenGroups->Groups[Index] = ...;
}

答案 2 :(得分:3)

第二个不包含元素,而是指向blah之后。所以如果你有这样的结构:

struct something
{
  int a, b;
  int c[0];
};

你可以这样做:

struct something *val = (struct something *)malloc(sizeof(struct something) + 5 * sizeof(int));
val->a = 1;
val->b = 2;
val->c[0] = 3;

在这种情况下,c将表现为具有5 int s的数组,但数组中的数据将位于something结构之后。

我正在处理的产品使用它作为一个大小的字符串:

struct String
{
  unsigned int allocated;
  unsigned int size;
  char data[0];
};

由于支持的体系结构,这将消耗8个字节加上allocated

当然所有这些都是C,但g ++例如毫无障碍地接受它。

答案 3 :(得分:1)

如果可以将应用程序限制为仅需要几个已知大小,则可以使用模板有效地实现灵活的数组。

template <typename BASE, typename T, unsigned SZ>
struct Flex : public BASE {
    T flex_[SZ];
};

答案 4 :(得分:1)

一项提案正在制定中,可能会纳入将来的C ++版本。 有关详细信息,请参见numpy.where(该提案是相当新的,因此可能会有所更改)

答案 5 :(得分:0)

如果你只想要

struct blah { int foo[]; };

那么你根本不需要这个结构,只需处理一个malloc&ed / new&#39; ed int数组。

如果您在开头有一些成员:

struct blah { char a,b; /*int foo[]; //not valid in C++*/ };

然后在C ++中,我想你可以用foo成员函数替换foo

struct blah { alignas(int) char a,b; 
    int *foo(void) { return reinterpret_cast<int*>(&this[1]); } };

使用示例:

#include <stdlib.h>
struct blah { 
    alignas(int) char a,b; 
    int *foo(void) { return reinterpret_cast<int*>(&this[1]); }
};
int main()
{
    blah *b = (blah*)malloc(sizeof(blah)+10*sizeof(int));
    if(!b) return 1;
    b->foo()[1]=1;
}

答案 6 :(得分:0)

我面临一个同样的问题,即声明一个可用于C ++代码的灵活数组成员。通过查看glibc标头,我发现灵活数组成员有一些用法,例如在struct inotify中声明如下(省略注释和一些不相关的成员):

struct inotify_event
{
  //Some members
  char name __flexarr;
};

__flexarr宏又定义为

/* Support for flexible arrays.
   Headers that should use flexible arrays only if they're "real"
   (e.g. only if they won't affect sizeof()) should test
   #if __glibc_c99_flexarr_available.  */
#if defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L
# define __flexarr  []
# define __glibc_c99_flexarr_available 1
#elif __GNUC_PREREQ (2,97)
/* GCC 2.97 supports C99 flexible array members as an extension,
   even when in C89 mode or compiling C++ (any version).  */
# define __flexarr  []
# define __glibc_c99_flexarr_available 1
#elif defined __GNUC__
/* Pre-2.97 GCC did not support C99 flexible arrays but did have
   an equivalent extension with slightly different notation.  */
# define __flexarr  [0]
# define __glibc_c99_flexarr_available 1
#else
/* Some other non-C99 compiler.  Approximate with [1].  */
# define __flexarr  [1]
# define __glibc_c99_flexarr_available 0
#endif

我对MSVC编译器并不熟悉,但是您可能不得不根据MSVC的版本再添加一个条件宏。

答案 7 :(得分:0)

标准 C++ 不支持灵活数组成员,但是 clang 文档说。

“除了此处列出的语言扩展之外,Clang 还旨在支持广泛的 GCC 扩展。”

C++ 的 gcc 文档说。

“GNU 编译器为 C++ 语言提供了这些扩展(您也可以在 C++ 程序中使用大多数 C 语言扩展)。”

C 文档的 gcc 文档支持零长度数组。

https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html

答案 8 :(得分:0)

灵活数组还不是 C++ 标准的一部分。这就是 int foo[]int foo[0] 可能无法编译的原因。虽然有一个 proposal 正在讨论中,但它尚未被 C++ (C++2b) 的最新修订版接受。

然而,几乎所有现代编译器都通过编译器扩展支持它。

问题是,如果您在最高警告级别 (-Wall --pedantic) 下使用此扩展程序,可能会导致警告。

对此的解决方法是使用包含一个元素的数组并进行越界访问。虽然这个解决方案是规范(dcl.arrayexpr.add)的 UB,但大多数编译器都会生成有效的代码,甚至 clang -fsanitize=undefined 对它也很满意:

#include <new>
#include <type_traits>

struct A {
    int a[1];
};

int main()
{
    using storage_type = std::aligned_storage_t<1024, alignof(A)>;
    static storage_type memory;
    
    A *ptr_a = new (&memory) A;

    ptr_a->a[2] = 42;
    
    return ptr_a->a[2];
}

demo


尽管如此,如果您希望代码符合标准并且不依赖于任何编译器扩展,则必须避免使用此功能。

答案 9 :(得分:-4)

更好的解决方案是将其声明为指针:

struct blah
{
    int* foo;
};

或者更好的是,将其声明为std::vector

struct blah
{
    std::vector<int> foo;
};