Q: I came across some code that declared a structure like this:
struct name { int namelen; char namestr[1]; };and then did some tricky allocation to make the namestr array act like it had several elements, with the number recorded by namelen. How does this work? Is it legal or portable?
A: It's not clear if it's legal or portable, but it is rather popular. An implementation of the technique might look something like this:
#include <stdlib.h> #include <string.h> struct name *makename(char *newname) { struct name *ret = malloc(sizeof(struct name)-1 + strlen(newname)+1); /* -1 for initial [1]; +1 for \0 */ if(ret != NULL) { ret->namelen = strlen(newname); strcpy(ret->namestr, newname); } return ret; }This function allocates an instance of the name structure with the size adjusted so that the namestr field can hold the requested name (not just one character, as the structure declaration would suggest).
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.)
Another possibility is to declare the variable-size element very large, rather than very small. The above example could be rewritten like this:
#include <stdlib.h> #include <string.h> #define MAXSIZE 100 struct name { int namelen; char namestr[MAXSIZE]; }; struct name *makename(char *newname) { struct name *ret = malloc(sizeof(struct name)-MAXSIZE+strlen(newname)+1); /* +1 for \0 */ if(ret != NULL) { ret->namelen = strlen(newname); strcpy(ret->namestr, newname); } return ret; }where MAXSIZE is larger than any name which will be stored. However, it looks like this technique is disallowed by a strict interpretation of the Standard as well. Furthermore, either of these ``chummy'' structures must be used with care, since the programmer knows more about their size than the compiler does.
Of course, to be truly safe, the right thing to do is use a character pointer instead of an array:
#include <stdlib.h> #include <string.h> struct name { int namelen; char *namep; }; struct name *makename(char *newname) { struct name *ret = malloc(sizeof(struct name)); if(ret != NULL) { ret->namelen = strlen(newname); ret->namep = malloc(ret->namelen + 1); if(ret->namep == NULL) { free(ret); return NULL; } strcpy(ret->namep, newname); } return ret; }(Obviously, the ``convenience'' of having the length and the string stored in the same block of memory has now been lost, and freeing instances of this structure will require two calls to free; see question 7.23.)
When the data type being stored is characters, as in the above examples, it is straightforward to coalesce the two calls to malloc into one, to preserve contiguity (and therefore rescue the ability to use a single call to free):
struct name *makename(char *newname) { char *buf = malloc(sizeof(struct name) + strlen(newname) + 1); struct name *ret = (struct name *)buf; ret->namelen = strlen(newname); ret->namep = buf + sizeof(struct name); strcpy(ret->namep, newname); return ret; }
However, piggybacking a second region onto a single malloc call like this is only portable if the second region is to be treated as an array of char. For any larger type, alignment (see questions 2.12 and 16.7) becomes significant and would have to be preserved.
C99 introduces the concept of a flexible array member, which allows the size of an array to be omitted if it is the last member in a structure, thus providing a well-defined solution.
References: Rationale Sec. 3.5.4.2
C9X Sec. 6.5.2.1