Submitter: Martin Uecker
Submission Date: 2015-04-01
Summary
This report is about the long-standing issue of not being able to convert a pointer to an array with certain element type to a pointer to an array with the corresponding constant-qualified element type. Although this seems similar, this is different to converting pointers to pointers to types with and without qualifiers, e.g. that it is unsafe to assign a value of type T** to a variable of const T** as illustrated in 6.5.16.1(6). The later is unsafe because there is an intermediate pointer which can be manipulated to write to a const-qualified object. Such a scenario is not possible for pointers to (one or multi-dimensional) arrays, where there is no such double indirection (and which are more similar to pointers to structs).In the current C standard, qualifiers are always attached to the element type of an array. It is not possible to declare an array type which is const:
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.The standard has an explicit rule which allows conversions of pointers to add qualifiers to the target type.
6.3.2.3(3): 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 second rule does not apply if the target is an array, because the qualifier is on the element type - not the array itself. This causes practical problems when using pointers to arrays. A practical example is the follow code for matrix transpose:
void transpose(int N, int M, double out[M][N], const double in[N][M])
{
for (int i = 0; i < N; i++)
for (int j = 0; j < M; j++)
out[j][i] = in[i][j];
}
const double a[2][2] = { ... };
double o[2][2];
transpose(2, 2, o, a); // Ok - because a is already constant
double b[2][2];
transpose(2, 2, o, b); // <-- passing incompatible pointer type
In practice, programmer work around this problem either by not
using the 'const' qualifier or by not using multi-dimensional arrays
and instead doing explicit pointer arithmetic. Both options are
error prone.
Other examples where the pointers to arrays with and without
qualifier on the element type are currently incompatible are
assignment, pointer subtraction, and conditional expressions:
double x[2][2];
const double (*xp)[2] = x; /* initialization from incompatible pointer */
double* v = 0;
const double* w = 0;
(void)(1 ? v : w); // ok - no array
const double z[2][2];
(void)(1 ? x : z); /* incompatible pointers */
Finally, the current behaviour allows unsafe writes to
arrays with const qualifier via conversion to a void pointer:
const int foo[5];
memset(&foo, 0, sizeof foo);
Suggested Technical Corrigendum
A. Change pointer conversion rules
B. Move qualifier to the array
Additional Information
1. Programs which become legal
2. Programs which beome illegal
void* v;
const int (*i)[3];
foo = (1 ? v : i); // <-- will have type 'const void*'
3. Compatibility with C++
The proposed changes would also increase compatibility with C++. The relevant part of the standard (according to draft N3690) which makes this work in C++ is:3.9.3(5) ... An array type whose elements are cv-qualified is also considered to have the same cv-qualifications as its elements.
4. Experience with GCC 5
5. Open Question