views:

1123

answers:

5

I have a piece of C code that looks like this:

const char (*foo)[2] = bar();

Now bar() is a function that returns a (const void *). How do I properly cast this const pointer? The code produces this warning from GCC : "initialization discards qualifiers from pointer target type". Here are some of my unsuccessful attempts:

const char (*foo)[2] = (const char *)bar();
const char (*foo)[2] = (const void **)bar();

The original code does work, I just can't get rid of the warnings by properly casting the return value.

EDIT : This has been suggested:

const char (*foo)[2] = (const char (*)[2])bar();

It appears to be correct, but GCC gives this warning : "cast discards qualifiers from pointer target type" which is nearly identical to the original warning.

EDIT 2 : OK, I think I've got it. The real problem here is the ( const void * ) definition of bar(). The const in the definition (const char( * )[2]) refers to the elements of the array, not the pointer to the array. This type definition is essentially an array, which when represented by a void pointer is not const. The real answer is that a ( const void * ) loses its const-ness when cast to (const char ( * )[2]).

+3  A: 

Try:

const char (*foo)[2] = (const char (*)[2])bar();

Edit but if bar returns a pointer to a const array of char pointers as your question title hints, there should be no need for a cast if you assign to a variable of this type:

char* const* foo = bar();
Charles Bailey
+1  A: 
const char (*foo)[2] = (const char (*)[2])bar();
reko_t
+1  A: 

Just a note, you don't need the array dimensions:

const void *bar() { 
    static const char a[10] = "abcdefghij";
    return &a[4];
}

int main() {
    const char (*foo)[2] = (const char (*)[])bar();
    return 0;
}

Since this might be hard for some to read:

cdecl> explain const char (*foo)[2]
declare foo as pointer to array 2 of const char
Robert S. Barnes
If he *knows* that the return pointer actually returns a pointer to an array of arrays of 2 char, rather than just a pointer to an array of 2 char, then by keeping the array dimensions he can do valid pointer arithmetic on `foo`.
Charles Bailey
Good point I suppose, but that would only be useful if he had a 2D array of these things allocated in a contiguous block of memory and he knows from some other source the bounds of that 2D array relative to the pointer being returned. Doesn't seem likely in this case. On the flip side, omitting the dimension probably doesn't buy him anything, it's mostly just trivia I suppose. By the way, he's returning a pointer to an array of 2 chars, not "an array of arrays of 2 char". That would be `const char (*foo)[][2]` or as cdecl says: declare foo as pointer to array of array 2 of const char
Robert S. Barnes
Arrays are usually passed around by pointers to their first element, hence why `const char(*foo)[2]` might point to an array of arrays of 2 char. Or a more familiar example: `\0` terminated strings are usually returned as `char*`, not `char(*)[]`.
Charles Bailey
+3  A: 

Several others have stated the correct cast, but it generates a spurious warning. That warning comes from a possible bug in the C standard, or (depending on your interpretation) a case that GCC should treat specially. I believe the const qualifier can be safely and unambiguously lifted to the array type. You can drop that warning with -Wno-cast-qual but of course that will eliminate warnings for cases that you actually care about.

To elaborate, the type const char (*)[2] means "pointer to array (length 2) of const char". The array is not marked const, just the elements of the array. When compared to the type const void *, the compiler notices that the latter is a pointer to const, where as the former is not, thus generating the warning. The C standard provides no way to mark an array as const, even though a const array would be equivalent to an array of const.

Jed
+1  A: 

The issue with the warning in the latest version of the cast has historical roots. You know that C language (as well as C++) correctly prohibits the T** -> const T** conversion. This is correct, since allowing this conversion would open the way for some subtle violations of const-correctness rules (can be found in any self-respecting FAQ).

However, C language also prohibits T** -> const T* const* conversion. This is different from C++, which allows this conversion. (This conversion doesn't violate const-correctness.) This has been long considered a "design defect" in C language specification. This defect has been "fixed" in C++, but it continues to persist in C (even in C99). Frankly, I have no idea why it was left unchanged in C99. One of the "fallouts" of that defect (or more precisely, of that approach to treating const-correctness) is that in C language the T (*)[N] -> const T (*)[N] conversion also remains prohibited, even though it carries no inherent threats to const-correctness.

I can't reproduce the warning you are getting with my version of GCC. But if you are getting it, it appears to be just another consequence of the same ideology. If you take into account that the conversion was requested by an explicit cast operator, the GCC warning is completely unjustified. You can try to work around the warning by using a chained cast

const char (*foo)[2] = (const char (*)[2]) (void *) bar();
AndreyT