No it doesn't, but this is only because the memory was allocated, and written into using a character type.
Memory is allocated using malloc. That object doesn't have declared1 type because it was allocated with malloc. Thus the object doesn't have any effective type.
Then the code accesses and modifies the object using the type char
. As the type is2 char
and no object having an effective type is copied5, copying doesn't set the effective type to char
for this and subsequent accesses, but sets the effective type to char
, only for the duration of the access3. After the access, the object doesn't have an effective type anymore.
Then the type int
is used to access and only read that object. As the object doesn't have an effective type, it becomes3 int
, for the duration of the read. After the access the object doesn't have an effective type anymore. As int
was obviously compatible with the effective type int
, the behavior is defined.
(Assuming the values read are not trap representation for int
.)
Had you accessed and modified the object using a non-character type that is also not compatible with int
, the behavior would be undefined.
Let's say your example was (assuming sizeof(float)==sizeof(int)
):
int i;
void *buf = calloc(5, sizeof(float)); // buf initialized to 0
{
float *ptr1 = buf;
for(i = 0; i < 5*sizeof(float); ++i)
ptr1[i] = (float)i;
}
int *ptr2 = buf;
for(i = 0; i < 5; ++i)
printf("%d", ptr2[i]);
The effective type of the object, when float
s are being written into, becomes of type float
, for the duration of the write and all subsequent accesses to the object that don't modify it2. When those objects are then accessed by int
the effective type remains float
, as the values are only being read not modified. The previous write using float
set the effective type to float
permanently until the next write into this object (which didn't happen in this case). Types int
and float
are not compatible4, thus the behavior is undefined.
(All text below is quoted from: ISO:IEC 9899:201x)
1 (6.5 Expressions 6)
The effective type of an object for an access to its stored value is the declared type of the object, if any. 87) Allocated objects have no declared type.
2 (6.5 Expressions 6)
If a value is stored into an object having no declared type through an lvalue having a type that is not a character type, then the type of the lvalue becomes the effective type of the object for that access and for subsequent accesses that do not modify the stored value.
3 (6.5 Expressions 6)
For all other accesses to an object having no declared type, the effective type of the object is simply the type of the lvalue used for the access.
4 (6.5 Expressions 8)
An object shall have its stored value accessed only by an lvalue expression that has one of
the following types: 88)
— 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.
5 (6.5 Expressions 6)
If a value is copied into an object having no declared type using memcpy or memmove, or is copied as an array of character type, then the effective type of the modified object for that access and for subsequent accesses that do not modify the value is the effective type of the object from which the value is copied, if it has one.
int
lvalue, has it not yet "become" anint
per 6.5 paragraph 6 when it's dereferenced via anint *
, thus violating strict aliasing after all? Parsing that paragraph is painful. – Benil