55

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

struct blah
{
    int foo[];
};

但是,当工作人员试图在 C++ 中使用 clang 编译一些代码时,该语法不起作用。(它一直在使用 MSVC。)我们必须将其转换为:

struct blah
{
    int foo[0];
};

翻阅 C++ 标准,我发现根本没有提到灵活的成员数组;我一直认为[0]这是一个无效的声明,但显然对于灵活的成员数组它是有效的。灵活的成员数组在 C++ 中真的有效吗?如果是这样,是正确的声明[]还是[0]

4

10 回答 10

32

C++ 于 1998 年首次标准化,因此它早于向 C 添加灵活的数组成员(这是 C99 中的新功能)。2003 年对 C++ 进行了更正,但没有添加任何相关的新特性。C++ (C++2b) 的下一个版本仍在开发中,似乎还没有添加灵活的数组成员。

于 2010-12-10T20:04:55.193 回答
28

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 对此写了一篇很好的文章:为什么有些结构以大小为 1 的数组结尾?

注意:在 Raymond Chen 的文章中,初始化“灵活”数组的示例中有一个错字/错误。它应该是:

for (DWORD Index = 0; Index < NumberOfGroups; Index++) { // note: used '<' , not '='
  TokenGroups->Groups[Index] = ...;
}
于 2010-12-10T20:31:21.827 回答
4

第二个将不包含元素,而是将指向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,它将表现为一个 5int秒的数组,但数组中的数据将在something结构之后。

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

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

由于受支持的架构,这将消耗 8 个字节加上allocated.

当然,这一切都是 C 语言,但例如 g++ 可以毫不费力地接受它。

于 2010-12-10T20:01:56.060 回答
4

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

template <typename BASE, typename T, unsigned SZ>
struct Flex : public BASE {
    T flex_[SZ];
};
于 2018-11-05T20:20:39.027 回答
3

如果你只想

struct blah { int foo[]; };

那么你根本不需要结构,你可以简单地处理一个 malloc'ed/new'ed int 数组。

如果您在开始时有一些成员:

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

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

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;
}
于 2017-11-30T13:22:19.117 回答
3

一项提案正在进行中,可能会成为未来的 C++ 版本。有关详细信息,请参阅http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1039r0.html(该提案相当新,因此可能会发生变化)

于 2019-08-23T13:17:33.857 回答
1

我在声明一个可以从 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版本再添加一个条件宏。

于 2019-10-13T06:24:22.063 回答
1

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

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

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

一种解决方法是使用具有一个元素的数组并进行越界访问。虽然这个解决方案是规范的 UB(dcl.arrayexpr.add),但大多数编译器都会生成有效代码,甚至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


话虽如此,如果您希望您的代码符合标准并且不依赖于任何编译器扩展,您将不得不避免使用此功能。
于 2021-06-08T20:25:33.830 回答
0

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

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

C++ 的 gcc 文档说。

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

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

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

于 2021-03-13T01:04:22.687 回答
-2

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

struct blah
{
    int* foo;
};

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

struct blah
{
    std::vector<int> foo;
};
于 2010-12-10T20:04:00.880 回答