In my day job, I have encountered a lot of C codes resembling the following pattern. I am worrying whether this pattern is safe.
typedef struct
{
unsigned char someField : 4;
unsigned char someOtherField : 4;
unsigned char body[1];
} __attribute__((__packed__, aligned(1))) SomeStruct;
int main()
{
unsigned char pack[16] = {};
SomeStruct* structPack = (SomeStruct*)pack;
structPack->someField = 0xC;
structPack->body[4] = 0x5;
return 0;
}
What makes me worry is that the program uses structPack->body[4]
, which is still a part of the 16-byte array, but is out-of-bound if we look at the definition of SomeStruct
. So there are two ways to look at it:
So, my questions are:
Note that this kind of code mostly runs on micro-controllers, and sometimes runs as application on Linux desktop.
Accessing an object through an incompatible lvalue is undefined behaviour. Alignment may be solved by your attribute line, but using the pointer to access the object is still violating strict aliasing:
unsigned char pack[16] = {};
SomeStruct* structPack = (SomeStruct*)pack;
6.5. p7:
An object shall have its stored value accessed only by an lvalue expression that has one of the following types:
— a type compatible with the effective type of the object,
— a qualified version of a type compatible with the effective type of the object,
— a type that is the signed or unsigned type corresponding to the effective type of the object,
— a type that is the signed or unsigned type corresponding to a qualified version of the effective type of the object,
— an aggregate or union type that includes one of the aforementioned types among its members (including, recursively, a member of a subaggregate or contained union), or
— a character type.
Where effective type is:
The effective type of an object for an access to its stored value is the declared type of the object, if any.
SomeStruct*
is not compatible to a char array.
The correct way of allocating SomeStruct is to use memory allocators, or alloca( which will allocate stack if that is a concern ) if the function is supported.
Still there is the problem of the body
member which is a size one array and Standard will not permit accessing it out of bounds( i.e. body[1] ). c99 introduced a solution which is the flexible array member:
typedef struct
{
unsigned char someField : 4;
unsigned char someOtherField : 4;
unsigned char body[]; //must be last
}...
When you set the size to allocate this struct, you add additional size depending how large the body[]
member needs to be.