Are flexible array members valid in C++?

2019-01-03 06:40发布

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]?

6条回答
干净又极端
2楼-- · 2019-01-03 07:11

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++0x) is still under development, and it seems flexible array members aren't added to it.

查看更多
地球回转人心会变
3楼-- · 2019-01-03 07:17

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];
};
查看更多
贼婆χ
4楼-- · 2019-01-03 07:21

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,b; 
    int *foo(void) { return reinterpret_cast<int*>(&this[1]); } };

Example use:

#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;
}
查看更多
Animai°情兽
5楼-- · 2019-01-03 07:24

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;
};
查看更多
Rolldiameter
6楼-- · 2019-01-03 07:29

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];
};

things are valid, and work quite well. 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] = ...;
}
查看更多
贼婆χ
7楼-- · 2019-01-03 07:33

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.

查看更多
登录 后发表回答