C99 added the "flexible array member" feature: the last element of a struct can be an array of size zero: "As a special case, the last element of a structure with more than one named member may have an incomplete array type; this is called a flexible array member." [C99, 6.7.2.1 ¶16]
In C90 code, the "struct hack" is implemented using an array of size [1] at the end of a struct. Some compilers allowed zero (like GNU C, I think) before C99.
In any case, supporting a [0]-sized array member which is not the last member of a struct, without issuing a diagnostic, is non-conforming. The example given in the article with consecutive [0] arrays requires a diagnostic.
By the way, the (apparently only?) advantage of the flexible array member is that we can use sizeof (type) to obtain the size of the structure just excluding the first element of the array. Whereas with the C90 style struct hack, we must use offsetof(type, last_member) so that we exclude [1] from the calculation:
struct foo {
/* ... */
int array[ZERO_OR_ONE];
};
/* Correct in C99, if ZERO_OR_ONE is 0
Incorrect in C90, (ZERO_OR_ONE can't be zero). */
size_t foo_plus_3_elems = sizeof (struct foo) + 3 * sizeof(int);
/* Correct in C99 whether or not ZERO_OR_ONE is 0 or 1.
Correct in C90 with ZERO_OR_ONE being 1. */
size_t foo_plus_3_elems = offsetof (struct foo, array) + 3 * sizeof(int);
Here, "incorrect" means we calculate slightly more storage than needed, usually without any downside."Correct in C90" means de facto correct, not that it was well-defined behavior. "Everyone" was doing it.
> If the size is not present, the array type is an incomplete type.
This is different from specifying the size as 0, which is still not allowed, per 6.7.5.2.1:
> In addition to optional type qualifiers and the keyword static, the [ and ] may delimit an expression or *. If they delimit an expression (which specifies the size of an array), the expression shall have an integer type. If the expression is a constant expression, it shall have a value greater than zero.
Oh, and for the record, while you may be alluding to this in your last paragraph: another advantage of flexible array members is that indexing them is actually well-defined per the spec (if enough space has been allocated, of course), while AFAIK the spec has never added a special case for length-1 arrays, so accessing the "extra elements" is technically undefined behavior. However, common compilers like GCC do treat length 1 specially, as an exception to optimizations that generally assume you won't index out of bounds, so 'technically' really is 'technically' - it's not something that some random future version of GCC is likely to break your code if you use.
The reason for this is that ... I don't use flexible array member myself or the [0] GCC extension, either.
Flexible array member doesn't seem to bring anything to the table other than formal definedness of behavior. I can't imagine any implementation which supports flexible-array member in its C99+ modes, which makes [1] fail in the same modes or in the C99 mode. In any old compiler that doesn't have C99 support, that formally undefined struct hack is all you have.
I don't mind using offsetof(type, member) to obtain the size of the header before the array, rather than sizeof(type).
The classic hack is portable to C90 and C++98, making it good for "Clean C".
That said, flexible array member is elegant in the sense that it just uses an incomplete array type similarly to a file scope array declaration. Using [1] is a bit like while (1) instead of for (;;). I don't like the [1] in the struct hack, but I don't use C for its beauty.
x.c:7:10: error: flexible array member not at end of structIf "everyone" is defined as "people using GCC."
C FAQ: [http://c-faq.com/struct/structhack.html]
"Despite its popularity, the technique is also somewhat notorious: Dennis Ritchie has called it ``unwarranted chumminess with the C implementation,'' and an official interpretation has deemed that it is not strictly conforming with the C Standard, although it does seem to work under all known implementations. (Compilers which check array bounds carefully might issue warnings.)"
EDIT: the FAQ actually refers to arrays of one element where GCC makes a big deal about saving memory by accepting zero-length arrays. But you are correct that this kind of cleverness wasn't limited to GCC ( https://blogs.msdn.microsoft.com/oldnewthing/20031212-00/?p=... , https://blogs.msdn.microsoft.com/oldnewthing/20040826-00/?p=... ).
The usual reason to use them is when you have a protocol header with a variable length data portion. Putting the zero length array in the struct allows you easy access to the data while not changing the sizeof() the header so you can avoid a bit of pesky pointer math and make the code easier to read.
Thinking about this some more, you could also use this as a ghetto form of union, but I'm not sure why you would want to.
struct
{
char[0] theBytes;
int theValue;
} /*
* SC C Compiler 8.8.4f1 (part of MPW 3.3) does not support zero sized
* arrays. I use this 'clever' trick involving the preprocessor that
* guarantees everything is aligned appropriately. It is possible with
* this that some space will be wasted between the header and the
* payload if the a single element of the payload is larger than the
* header, but that is not the case here.
*/
#if 0
struct state {
mbhdr_t st_mb; /* Screen 1, Line 1 */
short st_serln; /* Screen 1, Line 2 */
short st_astln;
unsigned char st_count;
unsigned char st_unit;
char st_qs[2];
char st_units; /* Screen 1, Line 3 */
avunit_t st_avms[0];
};
#else
struct state {
union {
struct {
mbhdr_t suh_mb; /* Screen 1, Line 1 */
short suh_serln; /* Screen 1, Line 2 */
short suh_astln;
unsigned char suh_count;
unsigned char suh_unit;
char suh_qs[2];
char suh_units; /* Screen 1, Line 3 */
} su_header;
avunit_t su_align[1];
} st_union[1];
};
#define st_mb st_union[0].su_header.suh_mb
#define st_serln st_union[0].su_header.suh_serln
#define st_astln st_union[0].su_header.suh_astln
#define st_count st_union[0].su_header.suh_count
#define st_unit st_union[0].su_header.suh_unit
#define st_qs st_union[0].su_header.suh_qs
#define st_units st_union[0].su_header.suh_units
#define st_avms st_union[1].su_align
#endifAn array of any size is best thought of as an array. Arrays are not pointers. (See section 6 of the comp.lang.c FAQ, http://www.c-faq.com/.)
A zero size array is best thought of as illegal (if you want portability) or as a compiler-specific extension (if you don't mind depending on a particular compiler, perhaps gcc).
Or, as I prefer to say, "correct".
> but using the zero length array trick creates clearer, more self documenting code. It's a tradeoff between readability and portability.
Clearer compared to which alternative? I find C99-style flexible array members (defined with "[]") quite clear.
You don't have to. C has flexible arrays in structs for this, and they actually work.
The term "ANSI C" is still very commonly used to refer to the language described by the 1989 ANSI C standard. This usage is strictly incorrect, but too firmly entrenched to ignore.
The 1990 ISO C standard describes the same language, and was officially adopted by ANSI, making the 1989 standard obsolete. The 1999 and 2011 ISO standards, both also officially adopted by ANSI, each officially made all earlier standards obsolete. If you want to refer to the language defined by the 1989 ANSI C standard (and described in K&R2), call it "C89" or "C90".
C11 might get more traction than C99 did, since gcc defaults to C11 plus GNU extensions starting with release 6. (But Microsoft's C compiler is still behind the times.)
Your prize for pointing out that the usage of ANSI C is incorrect is: disappointment in humanity.
Fortunately, these days VS2013+ do an OK job of C99.
C99 adds a feature called flexible array members. If the final member of a struct is an array with no length (blank rather than zero), you're allowed to use the array to access memory after the struct with the appropriate subscripts.
t_packet *packet = malloc(sizeof(*packet) + payload_size);I'd class that as a valid quibble, if my interpretation is correct, but a quibble nonetheless. The general intent is clear enough from the standard, I think: http://port70.net/~nsz/c/c11/n1570.html#6.7.2.1p18
Further reading on the subject of undefined behaviour (apropos of nothing in particular): http://port70.net/~nsz/c/c11/n1570.html#3.4.3p1, http://robertoconcerto.blogspot.co.uk/2010/10/strict-aliasin..., https://groups.google.com/forum/#!msg/boring-crypto/48qa1kWi..., http://blog.metaobject.com/2014/04/cc-osmartass.html
template <int N>
struct ion_system_heap {
struct ion_heap heap;
struct ion_page_pool *pools[N];
};
But that is not quite the same thing since ion_system_heap<10> and ion_system_heap<20> won't be the same type and makes it super hard to use. And a whole bunch of other problems like not knowing how many pages you want to allocate at compile time.Yes you can just use a vector for "pools" but that allocates it on the heap instead of in the struct.
Edit: But casting the allocated memory around to a struct type might violate aliasing rules...
uint16_t array[] = { 1, 2, 3, 4 }; cout << "Hello World " << 3[array] << endl;
are valid and well defined. Part of what makes memory access validation such a pain in the ass in C++ is that no attempts are really made at specifying rules for how you can come up with memory locations; just rules on which locations you can read and write from. You can do things like "((int)rand()) = 2", and nothing in the standard says this is an invalid thing to do, so long as you are lucky enough for rand() to point somewhere allocated.
Good times.
x = malloc(sizeof x + <variable_length))
x->zero_array points to the variable_length.
The advantage is that you get contiguous memory access and do a single malloc.
As opposed to:
x = malloc(sizeof x)
x->ptr = malloc(<variable_length>);
x and ptr are not necessarily contiguous.
struct{ int something; int someArray[]; }
and
struct{ int something; int someArray[0]; }
?
edit: clarifying, I thought int somearray [] was the same as int* somearray.
¯\_(ツ)_/¯