Is it okay for int** and const int** to alias?
Asked Answered
P

1

8

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;
Pittsburgh answered 16/7, 2017 at 1:8 Comment(10)
int *ip = (int *)cip; is not OK. Your compiler should complain about shedding the const. If not use higher warning levels. Or what kind of "OK" are you thinking of? "works", "works reliably", "works portably", "is good code", ...Pellegrino
@Yunnosch: No. It is perfectly OK. The explicit cast is actually how you tell the compiler that you really want to shed the const. That's how you tell the compiler not to issue any warnings. The first code snippet is perfectly legal C.Guenna
@AnT For the shown code, shedding the const is not needed. For other code shedding a const which has a purpose is not a good idea. I admit however, that I confused compiler warnings with the kind of nitpicky warnings generated by explicitly super-strict static code analysers; which I am to use for code in the job. They need more than a cast to stop nagging. It is kind of burnt into my thinking.Pellegrino
If your compiler/linker put ci in genuine read only storage then *ip = (int *)cip is an instant fault.Vacla
@Vacla That may be true but int *ip = ...; is very different from *ip = ...;. Just reading through a pointer should be fine, writing through it is undefined though.Pittsburgh
@Gilbert: There's no *ip = (int *)cip in the above code (that would make no sense at all, since *ip is int). As for int *ip = (int *)cip;... There's no fault of any kind in int *ip = (int *)cip; regardless of what kind of storage ci is stored in. The "fault" will occur if you do *ip = 56 after that. But int *ip = (int *)cip; is not only perfectly valid code, but a valuable implementational idiom widely used in C programming.Guenna
@Yunnosch: This is artificial code created just for the question. It is irrelevant whether shedding of const it is "needed" or "not needed" here. The very same shedding of const is used inside, say, standard strchr function. And there it is needed for rather obvious purpose.Guenna
@AnT Exactly, although a devils advocate could argue that the (char *) cast is only okay due to the "character type" exception, which is why I've used int everywhere in this example.Pittsburgh
@Tavian Barnes: I'm not sure what "character type exception" you are talking about. Casting away const has always been perfectly legal in C (and C++), regardless of type, as long as no attempt is made to modify a const object.Guenna
@AnT I was referring to the last bullet in the effective type rules. I agree that casting away const should be legal, but there is some doubt over whether the letter of the standard indeed allows it.Pittsburgh
G
5

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, 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.

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.

Gael answered 16/7, 2017 at 2:13 Comment(2)
The fact that a guarantee would only be useful if it were treated as an exception to aliasing rules no longer implies that compiler writers will interpret it as one. The Common Initial Sequence guarantees are basically useless under gcc and clang except in "no strict aliasing" mode. That having been said, neither compiler yet assumes that a int*p won't be accessed using an int const **p, nor vice versa; I don't know whether that will remain true.Nitride
Here is some evidence of GCC optimizing under the assumption that char * and unsigned char * don't alias: gcc.gnu.org/bugzilla/show_bug.cgi?id=85923Pittsburgh

© 2022 - 2024 — McMap. All rights reserved.