It is my understanding that something like this is okay:
const int ci = 42;
const int *cip = &ci;
int *ip = (int *)cip;
int j = *ip;
What about this?
const int ci = 42;
const int *cip = &ci;
const int **cipp = &cip;
int **ipp = (int **)cipp;
int j = **ipp;
The expression *ipp
is an lvalue of type int *
, however it is being used to access an object of effective type const int *
. (Namely, cip
).
According to the letter of the standard, it is a strict aliasing violation: the list of allowed types to alias does not include aliasing T *
as const T *
or vice versa.
The closest exception is this one: (C11 6.5/6 excerpt)
- a qualified version of a type compatible with the effective type of the object
"qualified version" is clearly defined by C11 6.2.5/26:
Each unqualified type has several qualified versions of its type, corresponding to the combinations of one, two, or all three of the
const
,volatile
, andrestrict
qualifiers. The qualified or unqualified versions of a type are distinct types that belong to the same type category and have the same representation and alignment requirements. A derived type is not qualified by the qualifiers (if any) of the type from which it is derived.
So the exception is that T
may be aliased as const T
and vice versa, but there is no similar exception for pointers to aliasable types. const T *
is not a qualified version of T *
.
However there is of course the footnote:
The intent of this list is to specify those circumstances in which an object may or may not be aliased
I couldn't say whether the intent of the rule is for const T *
and T *
to be aliasable or not. It seems unclear to me what the purpose of specifying that T *
and const T *
have "the same representation and alignment requirements" (6.2.5/28) would be if it is not aliasable.