灵活数组成员在 C++ 中有效吗?

发布于 2024-10-07 05:36:51 字数 390 浏览 3 评论 0原文

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

struct blah
{
    int foo[];
};

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

struct blah
{
    int foo[0];
};

纵观 C++ 标准,我发现根本没有对灵活成员数组的引用;我一直认为 [0] 是一个无效的声明,但显然对于灵活的成员数组来说它是有效的。灵活成员数组在 C++ 中实际上有效吗?如果是这样,正确的声明是 [] 还是 [0]

In C99, you can declare a flexible array member of a struct as such:

struct blah
{
    int foo[];
};

However, when someone here at work tried to compile some code using clang in C++, that syntax did not work. (It had been working with MSVC.) We had to convert it to:

struct blah
{
    int foo[0];
};

Looking through the C++ standard, I found no reference to flexible member arrays at all; I always thought [0] was an invalid declaration, but apparently for a flexible member array it is valid. Are flexible member arrays actually valid in C++? If so, is the correct declaration [] or [0]?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

日暮斜阳 2024-10-14 05:36:51

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

C++ was first standardized in 1998, so it predates the addition of flexible array members to C (which was new in C99). There was a corrigendum to C++ in 2003, but that didn't add any relevant new features. The next revision of C++ (C++2b) is still under development, and it seems flexible array members still aren't added to it.

作死小能手 2024-10-14 05:36:51

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

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

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

C++ doesn't support C99 flexible array members at the end of structures, either using an empty index notation or a 0 index notation (barring vendor-specific extensions):

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

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

As far as I know, C++0x will not add this, either.

However, if you size the array to 1 element:

struct blah
{
    int count;
    int foo[1];
};

the code will compile, and work quite well, but it is technically undefined behavior. You can allocate the appropriate memory with an expression that is unlikely to have off-by-one errors:

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)
}

So it's portable between C90, C99 and C++ and works just as well as C99's flexible array members.

Raymond Chen did a nice writeup about this: Why do some structures end with an array of size 1?

Note: In Raymond Chen's article, there's a typo/bug in an example initializing the 'flexible' array. It should read:

for (DWORD Index = 0; Index < NumberOfGroups; Index++) { // note: used '<' , not '='
  TokenGroups->Groups[Index] = ...;
}
北城孤痞 2024-10-14 05:36:51

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

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

If you can restrict your application to only require a few known sizes, then you can effectively achieve a flexible array with a template.

template <typename BASE, typename T, unsigned SZ>
struct Flex : public BASE {
    T flex_[SZ];
};
停顿的约定 2024-10-14 05:36:51

第二个不会包含元素,而是指向 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 的数组,但数组中的数据将在 something 结构之后。

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

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

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

当然,所有这些都是 C 语言,但是 g++ 可以顺利地接受它。

The second one will not contain elements but rather will point right after blah. So if you have a structure like this:

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

you can do things like this:

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

In this case c will behave as an array with 5 ints but the data in the array will be after the something structure.

The product I'm working on uses this as a sized string:

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

Because of the supported architectures this will consume 8 bytes plus allocated.

Of course all this is C but g++ for example accepts it without a hitch.

幻想少年梦 2024-10-14 05:36:51

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

A proposal is underway, and might make into some future C++ version.
See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1039r0.html for details (the proposal is fairly new, so it's subject to changes)

幽梦紫曦~ 2024-10-14 05:36:51

如果您只需要

struct blah { int foo[]; };

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

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

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

那么在 C++ 中,我想您可以将 foo 替换为 foo 成员函数:

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

使用示例:

#include <stdlib.h>
struct blah { 
    alignas(int) char a;
    char 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;
}

此类型不存在严格的别名问题此处将内存转换为初始结构,因为内存是动态的(没有声明的类型)。

If you only want

struct blah { int foo[]; };

then you don't need the struct at all an you can simply deal with a malloc'ed/new'ed int array.

If you have some members at the beginning:

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

then in C++, I suppose you could replace foo with a foo member function:

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

Example use:

#include <stdlib.h>
struct blah { 
    alignas(int) char a;
    char 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;
}

There's no strict aliasing issues with this type of casting of the memory past the initial struct here because the memory is dynamic (has no declared type).

春夜浅 2024-10-14 05:36:51

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

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

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

解决此问题的一种方法是使用包含一个元素的数组并进行越界访问。虽然此解决方案符合规范(dcl.array 和 < a href="https://timsong-cpp.github.io/cppwp/expr.add#4" rel="nofollow noreferrer">expr.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];
}

演示< /a>


Having all that said, if you want your code to be standard compliant and do not depend on any compiler extension, you will have to avoid using this feature.

Flexible arrays are not part of the C++ standard yet. That is why int foo[] or int foo[0] may not compile. While there is a proposal being discussed, it has not been accepted to the newest revision of C++ (C++2b) yet.

However, almost all modern compiler do support it via compiler extensions.

The catch is that if you use this extension with the highest warning level (-Wall --pedantic), it may result into a warning.

A workaround to this is to use an array with one element and do access out of bounds. While this solution is UB by the spec (dcl.array and expr.add), most of the compilers will produce valid code and even clang -fsanitize=undefined is happy with it:

#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


Having all that said, if you want your code to be standard compliant and do not depend on any compiler extension, you will have to avoid using this feature.

不再见 2024-10-14 05:36:51

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

I faced the same problem to declare a flexible array member which can be used from C++ code. By looking through glibc headers I found that there are some usages of flexible array members, e.g. in struct inotify which is declared as follows (comments and some unrelated members omitted):

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

The __flexarr macro, in turn is defined as

/* 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

I'm not familar with MSVC compiler, but probably you'd have to add one more conditional macro depending on MSVC version.

瀟灑尐姊 2024-10-14 05:36:51

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

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

C++ 的 gcc 文档说。

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

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

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

Flexible array members are not supported in standard C++, however the clang documentation says.

"In addition to the language extensions listed here, Clang aims to support a broad range of GCC extensions."

The gcc documentation for C++ says.

"The GNU compiler provides these extensions to the C++ language (and you can also use most of the C language extensions in your C++ programs)."

And the gcc documentation for C documents support for arrays of zero length.

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

浊酒尽余欢 2024-10-14 05:36:51

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

struct blah
{
    int* foo;
};

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

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

The better solution is to declare it as a pointer:

struct blah
{
    int* foo;
};

Or better yet, to declare it as a std::vector:

struct blah
{
    std::vector<int> foo;
};
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文