Clarification: My question is:
int
to access an object of effective type const int
?This question has two code samples which use an lvalue of type int
to access an object of effective type const int
, and my intent is to achieve this with as little distraction as possible. If there is any other source of UB besides this specific problem please leave a comment and I will try to update the code sample.
Here is a specific code example for discussion:
#include <stdio.h>
#include <stdlib.h>
int main()
{
const int c = 5;
printf("%d\n", *(int *)&c);
}
The reason I think it might be UB is that the strict aliasing rule seems to say that it is UB:
C11 6.5/7
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,
- ...
The effective type of the object (6.5/6) here is const int
.
First bullet point: int
and const int
are not compatible types (6.2.7/1, 6.7.3/10).
Second bullet point: int
does not seem to be a qualified version of const int
, because we didn't produce it by adding qualifiers. However 6.2.5/26 is unclear:
Each unqualified type has several qualified versions of its type, corresponding to the combinations of one, two, or all three of the const, volatile, and restrict 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.
It doesn't define what a "qualified version of const int
" would be, it only defines the term "qualified version" when applied to an unqualified type.
Second code sample:
int *pc = malloc(sizeof *pc);
memcpy(pc, &c, sizeof c);
printf("%d\n", *pc); // UB?
Since memcpy
preserves the effective type (6.5/6) , reading through *pc
has exactly the same interaction with the strict aliasing rule as reading through *(int *)&c
does in the first example.
It is not. What you have found is why it cannot be implicitely cast.
[6.2.5/26] states:
Each unqualified type has several qualified versions of its type, corresponding to the combinations of one, two, or all three of the const, volatile, and restrict 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.
(Note: each unqualified type. const int
is not unqualified but int
is unqualified.)
With footnote:
The same representation and alignment requirements are meant to imply interchangeability as arguments to functions, return values from functions, and members of unions.
This means reading it will work the same way and yield the same value.
[6.7.3/6] specifies UB only for modifications:
If an attempt is made to modify an object defined with a const-qualified type through use of an lvalue with non-const-qualified type, the behavior is undefined.
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