Say I have the following structures:
struct StructA
{
int some_member;
double some_other_member;
};
struct StructB
{
char a_char;
char another_char;
};
Now say I have some buffer in memory that contains a bunch of these structures, and I would like to parse it. Each structure in the buffer is preceded by an int
describing the type of the structure. Like this:
struct Descriptor
{
int type;
union
{
struct StructA a;
struct StructB b;
} data;
};
What I would like to do, is to cast the pointer to the buffer (char *
) to struct Descriptor *
, read the type, then access the correct member of the union, then advance the pointer by the size of the type member + the size of the correct union member (for example, sizeof(int) + sizeof(struct StructA)
).
However, will this be valid C code? (In the sense that is does not invoke undefined behaviour.) My main concern is that the memory buffer may be smaller than sizeof(struct Descriptor)
because it contains only StructB
and the preceding type field.
Is it legal to utilize a pointer to a structure when the underlying memory is not sufficient to hold the entire structure, even though I'm accessing only the valid portion of the memory? Edit: If it is not valid for a structure, is it valid for a union?
If not, is there a better way? I found this comment, and the answer to which it is made, but it deals with an array of the full size of the structure.
The size of the union is based on the size of the largest member of the union. Let's understand through an example. As we know, the size of int is 4 bytes, size of char is 1 byte, size of float is 4 bytes, and the size of double is 8 bytes.
The size of a structure is the sum of the size of all data members and the packing size. The size of the union is the size of its data member, which is the largest in size. Only the latest initialized data member stores the value. Only one data member can be initialized at a time.
The size of a structure is equal or greater to the sum of the sizes of each data member. When the variable is declared in the union, the compiler allocates memory to the largest size variable member. The size of a union is equal to the size of its largest data member size.
They are both container data types, and they are capable of holding any data type. Although there is one very major difference between them. The union has the same memory location for all of its members, while the Structure possesses separate ones for the same purpose.
No, using simple sizeof to calculate the offset is not correct because it doesn't take the possible padding into consideration.
You should use the offsetof macro defined in stddef.h, which will work with any padding:
struct Descriptor* s = /*member data.a is initialized*/;
const size_t aoff = offsetof( struct Descriptor , data.a );
const size_t doff = offsetof( struct StructA , some_other_member );
double* p = ( double* )( ( unsigned char* )s + aoff + doff );
p
now points to the member some_other_member
in member data.a
of s
.
I think it's valid as long as you dereference the pointer correctly, i.e. don't touch the data beyond the buffer size which I assume you know beforehand and deal with alignment properly. So you just need to worry about handling the memory buffer correctly, other than that this is what the standard has to say about such conversion
6.3.2.3 Pointers
- A pointer to an object type may be converted to a pointer to a different object type. If the resulting pointer is not correctly aligned68) for the referenced type, the behavior is undefined. Otherwise, when converted back again, the result shall compare equal to the original pointer. When a pointer to an object is converted to a pointer to a character type, the result points to the lowest addressed byte of the object. Successive increments of the result, up to the size of the object, yield pointers to the remaining bytes of the object.
68)In general, the concept ‘‘correctly aligned’’ is transitive: if a pointer to type A is correctly aligned for a pointer to type B, which in turn is correctly aligned for a pointer to type C, then a pointer to type A is correctly aligned for a pointer to type C.
Then, this is what it says about the assignment
6.5.16.1 Simple assignment
Semantics
- If the value being stored in an object is read from another object that overlaps in any way the storage of the first object, then the overlap shall be exact and the two objects shall have qualified or unqualified versions of a compatible type; otherwise, the behavior is undefined.
So if the overlap is exact you are just fine and the behavior is defined.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With