Yes, this is legal. To see why, you don't even need to think about strict aliasing rule, because it doesn't apply in this case.
According to the C99 standard, when you do this:
int* i = malloc( sizeof( int ) + sizeof( float ) + MAX_PAD ) ;
*i = 456 ;
malloc
will return a pointer to a memory block large enough to hold an object of size sizeof(int)+sizeof(float)+MAX_PAD
. However, notice that you are only using a small piece of this size; in particular, you are only using the first sizeof(int)
bytes. Consequently, you are leaving some free space that can be used to store other objects, as long as you store them into a disjoint offset (that is, after the first sizeof(int)
bytes). This is tightly related with the definition of what exactly is an object. From C99 section 3.14:
Object: region of data storage in the execution environment, the
contents of which can represent values
The precise meaning of the contents of the object pointed to by i
is the value 456
; this implies that the integer object itself only takes a small portion of the memory block you allocated. There is nothing in the standard stopping you from storing a new, different object of any type a few bytes ahead.
This code:
float* f = ( float* )( ( char* )i + sizeof( int ) + PaddingBytesFloat( (char*)i ) ) ;
*f= 2.71f ;
Is effectively attaching another object to a sub-block of the allocated memory. As long as the resulting memory location for f
doesn't overlap with that of i
, and there is enough room left to store a float
, you will always be safe. The strict aliasing rule doesn't even apply here, because the pointers point to objects that do not overlap - the memory locations are different.
I think the key point here is to understand that you are effectively manipulating two distinct objects, with two distinct pointers. It just so happens that both pointers point to the same malloc()
'd block, but they are far enough from one another, so this is not a problem.
You can have a look at this related question: What alignment issues limit the use of a block of memory created by malloc? and read Eric Postpischil's great answer: https://stackoverflow.com/a/21141161/2793118 - after all, if you can store arrays of different types in the same malloc()
block, why wouldn't you store an int
and a float
? You can even look at your code as the special case in which these arrays are one-element arrays.
As long as you take care of alignment issues, the code is perfectly fine and 100% portable.
UPDATE (follow-up, read comments below):
I believe your reasoning about the standard not enforcing strict aliasing on malloc()
'd objects is wrong. It is true that the effective type of a dynamically allocated object can be changed, as conveyed by the standard (it is a matter of using an lvalue expression with a different type to store a new value in there), but note that once you do that, it is your job to ensure that no other lvalue expression with a different type will access the object value. This is enforced by rule 7 on section 6.5, and you quoted it in your question:
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;
Thus, by the time you change the effective type of an object, you are implicitly promising to the compiler that you won't access this object using an old pointer with an incompatible type (compared to the new effective type). This should be enough for the purposes of the strict aliasing rule.