In a C module (aka, compilation unit), I want to have some private data, but expose it read-only to the outside world. I achieve that by having a field in a s
I am not entirely sure if this could be considered a good idea, but something that I do sometimes when I'm feeling lazy is to define a "const cast" macro like:
#define CC(_VAR) ((const typeof(_VAR))(_VAR))
This of course assumes that you have a compiler that supports the typeof
extension. Using this (dubious) construction you could then write
const shape *fooshapes(const foo *f)
{
return CC(f->shapes);
}
Otherwise, C doesn't generally cast to const implicitly. You do have to explicitly cast pointers to const pointers.
pointer to array not compatible to a pointer to 'const' array?
Yes they are incompatible, you can't change a nested const qualifier, int (*a)[42]
is not compatible with int const (*b)[42]
related, as double **
is not compatible with double const **
Is my assumption correct that this doesn't lead to a hole in const correctness?
Well, you are adding const qualifier so no. In fact, return non const don't produce a warning and don't break const correctness according to the same rule that doesn't allow you to add const. But code like that are very confuse but are not undefined behavior.
shape *fooshapes(const foo *f)
{
return f->shapes;
}
Does the explicit cast violate the standard here?
Strictly, Yes, as compiler said there types are incompatibles, however I don't think it would produce a bug in any actual classic hardware.
The thing is the standard don't guarantee that double *a
and double const *b
have the same size or even the same value but just guarantee that a == b
will produce a positive value. You can promote any double *
to double const *
but like I said you must promote it. When you cast you don't promote anything because the array/pointer is nested. So it's undefined behavior.
It's a "you simply can't do that in C", you could ask but "what I'm suppose to do ?". Well, I don't see the purpose of your structure neither of your pointer to array in the structure. To fix the deeper problem of your data structure, you should ask an other question where you talk more about what is your purpose with this code. For example, your function could do something like that:
uint8_t const *fooshapes(const foo *f, size_t i)
{
return f->shapes[i];
}
The issue is that by typedef:ing an array and then const
-qualifying a pointer to that type, you actually get an const uint8_t(*)[64]
, which is not compatible with uint8_t(*)[64]
1). Const correctness and array pointers behave awkwardly together, see this for an example of the same issue.
Anyway, the root of the problem in this specific case is hiding an array behind a typedef. This is usually not a good idea. You can fix this by wrapping the array inside a struct instead, which might also give a better design overall. Example:
typedef struct shape
{
uint8_t shape[64];
} shape_t;
typedef struct foo
{
shape_t shapes;
} foo_t;
Now you can return a const shape_t*
just fine.
Optionally you can now either make shape_t
an opaque type just like foo_t
. Or you can make the internals of shape_t
public by for example exposing the struct declaration in a public header shape.h.
1) Implicit conversion between a pointer-to-type and a qualified-pointer-to-type is the only allowed implicit conversion.
C11 6.3.2.3/2
For any qualifier q, a pointer to a non-q-qualified type may be converted to a pointer to the q-qualified version of the type; the values stored in the original and converted pointers shall compare equal.
This does not apply here. For the conversion to be ok, it would have to be a conversion from pointer-to-array-type to pointer-to-qualified-array-type.
But it is not, it is a conversion from pointer to-array-type to qualified-pointer-to-array-type.
Normative text for compatible types in C is chapter 6.2.7, which only references further to 6.7.3. Relevant parts:
C11 6.7.3/9
If the specification of an array type includes any type qualifiers, the element type is so-qualified, not the array type.
and C11 6.7.3/10
For two qualified types to be compatible, both shall have the identically qualified version of a compatible type
This is why gcc correctly issues a diagnostic message - the pointers are not identically qualified versions.
It is indeed the same double pointer issue you refer to in the question.
You can convert pointer-to-T to pointer-to-const-T. But const
applied to an array qualifies the element type, not the array type (C11 6.7.3.9), so that's not what you're trying to do here. You're not trying to convert pointer-to-array-of-T to pointer-to-const-array-of T, but rather trying to convert to pointer-to-array-of-const-T, and those two types are not compatible in C.