问题
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]
?
回答1:
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.
回答2:
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] = ...;
}
回答3:
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 int
s 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.
回答4:
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];
};
回答5:
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;
}
回答6:
A proposal is underway, and might make into some future C++ version. See https://thephd.github.io/vendor/future_cxx/papers/d1039.html for details (the proposal is fairly new, so it's subject to changes)
回答7:
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.
回答8:
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;
};
来源:https://stackoverflow.com/questions/4412749/are-flexible-array-members-valid-in-c