Reputation: 1238
I was reading this version of the C99 standard linked by Wikipedia to try to understand how flexible array members work.
In section 6.7.2.1, this struct was declared:
struct s { int n; double d[]; };
And an example was given:
s1 = malloc(sizeof (struct s) + 10);
s2 = malloc(sizeof (struct s) + 6);
Where it said that s1
and s2
would behave as if the declarations were:
struct { int n; double d[1]; } *s1, *s2;
and it listed some things you can do:
double *dp;
dp = &(s1->d[0]); // valid
*dp = 42; // valid
dp = &(s2->d[0]); // valid
*dp = 42; // undefined behavior
I can see why the last line above is undefined since s2
was only allocated 6 extra bytes which is not enough to store a double, but then I don't understand why it would say that the behaviour of s1
and s2
would be like if they were declared as:
struct { int n; double d[1]; } *s1, *s2;
When it seems like s2
has not been allocated enough memory to store that struct.
The document seems to be some kind of draft so I'm not sure if there's been an error or if I'm misunderstanding what is meant.
Upvotes: 0
Views: 155
Reputation: 794
I like to treat flexible structure in C99 like virtual classes in C++, you can point at them but you shouldn't instantiate them. I often build a helper function (factory helper?) that takes the number of items stored in the flexible array and returns the actual allocations needed for the structure just to make sure it is clear about the intent.
#define OBJECTS_NEEDED 10
typedef struct
{
uint8_t val1;
uint32_t val2; // probable forces 4byte alignment of structure
} myObject_t;
typedef struct
{
uint8_t allocatedObjects;
uint16 someMetaData;
myObject_t objects[]; // struct inherits worst case alignment rule
} myObjectSet_t;
size_t getMyObjectSetSize(uint8_t reqObjs)
{
return sizeof(myObjectSet_t) + reqObjs * sizeof(myObject_t);
}
void initMyObjectSetSize(myObjectSet_t *mySet, uint8_t reqObjs)
{
mySet->allocatedObjects = reqObjs;
// Other Init code .....
}
void main()
{
myObjectSet_t *mySet = malloc(getMyObjectSetSize(OBJECTS_NEEDED));
initMyObjectSetSize(mySet , OBJECTS_NEEDED);
// One issue, you can't rely on structure padding to be the same
// from machine to machine so this test may fail on one compiler
// and pass on another. You really do nee to use offsetof() if
// you need to find address of mySet given the address of one
// of the objects[]
assert((void*)mySet->objects == (void*)(mySet + 1));
}
The compiler knows sizeof(myObjectSet_t) needs to be at least mod 4 because an array of myObject_t probable needs at least 32bit alignment. I have seen the GNU compiler running on Windows generate different padding from the GNU compiler running in a virtual machine on the same laptop.
Upvotes: 0
Reputation: 78903
(You shouldn't be looking into C99 anymore, it is obsolete. C11 is document n1570 at the same place that your are citing. It will probably/hopefully soon be replaced by C17.)
The reason, I think, that it says it behaves as if it had one element is the phrase
If it would have no elements, such an array behaves as if it had one element but the behavior is undefined if any attempt is made to access that element...
Upvotes: 1