Clarification: My question is:
- Is it UB to use an lvalue of type
int
to access an object of effective typeconst 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.
SGIBUS
or aSIGTERM
(its been a few years since it happened). Mess with const-ness at your own peril :) – Huda