tags:

views:

1653

answers:

3

Paragraph 6.7.3.8 of the C99 spec states

If the specification of an array type includes any type qualifiers, the element type is so-qualified, not the array type. If the specification of a function type includes any type qualifiers, the behavior is undefined.

In the rationale (logical page 87, physical page 94), an example of casting a flat pointer to a (variable length) array pointer is given.

void g(double *ap, int n)
{
    double (*a)[n] = (double (*)[n]) ap;
    /* ... */ a[1][2] /* ... */
}

Certainly if the array ap is not modified within the function, it should be marked const, however the cast in

void g(const double *ap, int n)
{
    const double (*a)[n] = (const double (*)[n]) ap;
    /* ... */
}

does not preserve the const qualifier since (per 6.7.3.8) it applies to the elements of the target instead of the target itself, which has array type double[n]. This means that compilers will rightly complain if given the appropriate flags (-Wcast-qual for GCC). There is no way to denote a const array type in C, but this cast is very useful and "correct". The -Wcast-qual flag is useful for identifying misuse of array parameters, but the false positives discourage its use. Note that indexing a[i][j] is both more readable and, with many compilers, produces better machine code than ap[i*n+j] since the former allows some integer arithmetic to be hoisted out of inner loops with less analysis.

Should compilers just treat this as a special case, effectively lifting qualifiers from the elements to the array type to determine whether a given cast removes qualifiers or should the spec be amended? Assignment is not defined for array types, so would it hurt for qualifiers to always apply to the array type rather than just the elements, in contrast to 6.7.3.8?

A: 

The situation is awkward with pointers (ie, arrays), but here's my recollection of the details:

const double *ap is a pointer to a constant double;

double *const ap is a constant pointer to a double;

const double *const ap is a constant pointer to a constant double;

So I believe it is possible to do what you're asking, although I've not tried this in years -- the gcc option you're using wasn't available the last time I did this!

EDIT: This answer is not correct for the question - I'm leaving it to preserve the comments below, which clarify the problem for mere mortals (or rusty C developers...)

Ken Gentle
My C is rusty, but why doesn't `const double *const ap` in the method signature do exactly what he's requesting? Constant array reference and Constant Element references?
Ken Gentle
No, the consts in the example all refer to the elements, not the pointers. A quirk of the language (or compiler?) makes it look as though the const qualifier on the elements is not preserved, when logically it is preserved.
finnw
The second `const` is irrelevant since we don't care about changing the value of the pointer `ap`. We want to cast a "pointer to const double" to "pointer to const array of double" this cannot be expressed in C so instead we cast to "pointer to array of const double" which triggers the warning.
Jed
> the gcc option you're using wasn't available the last time I did thisBTW, -Wcast-qual has been in gcc at least since 2.5.8 (1993).
Jed
@Jed - Should give you a hint as to the last time I did this! Seriously, it probably was some time in the mid- to late- 90s, so support of 2.5.8 or earlier for our products at the time is very likely.
Ken Gentle
+1  A: 

Possible workaround for the C programmer (but not the compiler designer):

gcc with -Wcast-qual does not complain about this:

void g(const double *ap, int n)
{
    int i;
    struct box 
    {
      double a[n];
    };
    const struct box *s = (const struct box *)ap;

    for (i=0; i<n; ++i)
    {
       doStuffWith(s->a[i]);
       /* ... */
    }
}

Even if it's not very elegant. The trailing array member a also has a slightly different meaning between C89 and C99, but at least you get the intended effect.

finnw
It would actually look like `s[i].a[j]` which the compiler could probably optimize in the same way as `a[i][j]`. This solution avoids the warning, but it adds significant obfuscation, particularly for 4-dimensional arrays which I use.
Jed
+4  A: 

This is a known issue that has been discussed several times over the last 10 years at comp.std.c. The bottom line is that the specific case you presented is not currently legal in Standard C, you need to either remove the qualifier or refrain from using a pointer to an array to refer to the qualified elements in the array.

If you think you have a good idea to overcome the issue, you can post it to news:comp.std.c for discussion. If others agree that it is a good idea you or someone else can file a defect report to have the behavior changed (there are several committee members that frequent comp.std.c so feedback from the people who would potentially be reviewing the DR would be useful to have prior to filing it). I think there may be some issues with your proposal to have qualifiers affect the array itself but I'd have to give it some more thought.

Robert Gamble
Thanks, I realize I'm not the first person to run into this. When I searched comp.std.c, I found several cases where people wanted a similar assignment without an explicit cast. With the cast, I think the example is perfectly safe and standards conforming, although it looks wrong to the compiler.
Jed
C99 added several features to make C more appealing for numerics, especially VLA and restrict. In lieu of the standard addressing this case, it would be nice if compilers could determine that the cast was actually safe.
Jed
It may be "safe" in the sense that it doesn't works as expected with existing implementations but based on the way the standard is worded today, I think the compilers are correct and this technically isn't valid C code.
Robert Gamble
For what it's worth, compilers only complain when special flags are given (-Wcast-qual for GCC, not included in -Wall -Wextra). Such flags are not available in current Intel and PGI compilers. My thought was that the spec could make const-lifting precise so the cast would not "look dangerous".
Jed
One issue maybe is that if arrays are qualified by their element qualifiers - what to do for rvalue arrays? Rvalues are not qualified, but if that's so, and if an rvalue array has a const element type, then in those cases an element type would *not* affect the array qualifiers. This is very nasty, and actually we have a similar situation in C++: Some compilers (those based on EDG), strip qualifiers from array elements if the array is an rvalue: `struct A { char const c[5]; A():c() { } }; int main() { A().c[0] = 'a'; }` <- this is valid on those implementations.
Johannes Schaub - litb
Because C++ says that the same as C - that cv qualifiers applied to an array qualify the elements instead of the array. But then, it says that an array type can be said to be "more or less qualified" than other types based on its element type. This is actually quite tricky, i think.
Johannes Schaub - litb