const char ** incompatible with char ** (and vice versa)?
Author |
Message |
p.. #1 / 14
|
 const char ** incompatible with char ** (and vice versa)?
Greetings all. This question may sound like a nit-picker but it's popped up a few times in the "real world" and nobody I've come across has been able to answer it with any sort of decent reasoning. Using gcc 2.95.2 under GNU/Linux and compiling the following snippit of code: int main(void) { const char **p2p_const; char **p2p; p2p_const=p2p; return 0; Quote: }
(char could be replaced with any other type) The compiler emits the warning: foo.c: In function `main': foo.c:6: warning: assignment from incompatible pointer type This is annoying more than anything, especially went code is compiled such that warnings are treated like errors. Using a const char ** cast gets rid of the warning but what I (sadly) fail to realize is why, in the first place, the two pointer types are incompatible. Anybody with an enlightening explanation? Please share. :) Regards, kw Sent via Deja.com http://www.*-*-*.com/
|
Tue, 17 Jun 2003 05:51:05 GMT |
|
 |
Ben Pfaf #2 / 14
|
 const char ** incompatible with char ** (and vice versa)?
Quote:
> int > main(void) > { > const char **p2p_const; > char **p2p; > p2p_const=p2p; > return 0; > } > foo.c: In function `main': > foo.c:6: warning: assignment from incompatible > pointer type
Ah, yes, this ugly, annoying problem catches me out from time to time too. The FAQ has the answer: 11.10: Why can't I pass a char ** to a function which expects a const char **? A: You can use a pointer-to-T (for any type T) where a pointer-to- const-T is expected. However, the rule (an explicit exception) which permits slight mismatches in qualified pointer types is not applied recursively, but only at the top level. You must use explicit casts (e.g. (const char **) in this case) when assigning (or passing) pointers which have qualifier mismatches at other than the first level of indirection. References: ISO Sec. 6.1.2.6, Sec. 6.3.16.1, Sec. 6.5.3; H&S Sec. 7.9.1 pp. 221-2. -- "I should killfile you where you stand, worthless human." --Kaz
|
Tue, 17 Jun 2003 06:12:06 GMT |
|
 |
Kaz Kylhe #3 / 14
|
 const char ** incompatible with char ** (and vice versa)?
Quote:
>int >main(void) >{ > const char **p2p_const; > char **p2p; > p2p_const=p2p; > return 0; >} >(char could be replaced with any other type) >The compiler emits the warning: >foo.c: In function `main': >foo.c:6: warning: assignment from incompatible >pointer type
This diagnostic is required by ANSI C, because a pointer to a const const char and a pointer to char are not compatible types. The C conversion rules allow an assignment from unqualified T * to const T *, even though the two types are incompatible. Similar freedom applies to initialization of objects such as function parameters, and returning values from functions. However, this special allowance does not extend to pointers derived from these two types: you cannot assign an unqualified T ** to an lvalue of type const T **. In principle, such a conversion should be safe, so in some ways this is a pathological consequence of the language rules, which deem differently qualified types to be incompatible, even though they share the same representation. The rules do prohibit certain programs from being considered correct, for example a program which in one translation unit contains the external definition ``const int x = 3;'' and in another unit the declaration ``extern int x''. The behavior of this program is undefined if the reference is used, because its type is incompatible with that of the definition. Quote: >This is annoying more than anything, especially >went code is compiled such that warnings are >treated like errors. >Using a const char ** cast gets rid of the warning >but what I (sadly) fail to realize is why, in the >first place, the two pointer types are >incompatible.
Because if they were allowed to be compatible, it could open up certain holes in the language, like a struct foo { const int x, y; }; in one translation unit being compatible with struct bar { int x, y; }; which is open to abuses. Of course, a C program which does this type of thing anyway is nevertheless widely portable due to the lack of run time diagnosis for this type of thing. I'm finding it difficult to enumerate the implications that such a change would have; if we have a longer comp.lang.c debate about this I'm sure a lot of good reasons will come to light for differently qualified types being incompatible.
|
Tue, 17 Jun 2003 06:25:17 GMT |
|
 |
Ben Pfaf #4 / 14
|
 const char ** incompatible with char ** (and vice versa)?
[discussing incompatibility of const T ** and T ** for type T] Quote: > Because if they were allowed to be compatible, it could open up certain holes > in the language, like a > struct foo { > const int x, y; > }; > in one translation unit being compatible with > struct bar { > int x, y; > }; > which is open to abuses. Of course, a C program which does this type of > thing anyway is nevertheless widely portable due to the lack of run time > diagnosis for this type of thing.
I don't think that making const T ** and T ** compatible is a great idea either. I think that making the latter acceptable for assignment to the former is a better idea. Such a change could be made just by modifying the constraint in C99 section 6.15.16.1 "Simple assignment" from -- both operands are pointers to qualified or unqualified versions of compatible types, and the type pointed to by the left has all the qualifiers of the type pointed to by the right; to a more general test, and similarly for the next constraint in the list. This change wouldn't open up the hole that you pointed out above AFAICS. Quote: > I'm finding it difficult to enumerate the implications that > such a change would have; if we have a longer comp.lang.c > debate about this I'm sure a lot of good reasons will come to > light for differently qualified types being incompatible.
Agreed. (BTW, Kaz, your lines are getting a bit long even after just one quoting, so I've reflowed the paragraph above.) -- "This is a wonderful answer. It's off-topic, it's incorrect, and it doesn't answer the question." --Richard Heathfield
|
Tue, 17 Jun 2003 06:41:00 GMT |
|
 |
Keith Warn #5 / 14
|
 const char ** incompatible with char ** (and vice versa)?
Quote: > Ah, yes, this ugly, annoying problem catches me out from time to > time too. The FAQ has the answer:
but only looked at the "pointers", "arrays and pointers", and "strange problems" sections; I didn't think to look in the "standard C" section. :/ Thanks again! kw Sent via Deja.com http://www.deja.com/
|
Tue, 17 Jun 2003 06:49:52 GMT |
|
 |
Jeff Robertso #6 / 14
|
 const char ** incompatible with char ** (and vice versa)?
Quote:
> >Using a const char ** cast gets rid of the warning > >but what I (sadly) fail to realize is why, in the > >first place, the two pointer types are > >incompatible. > Because if they were allowed to be compatible, it could open up certain holes > in the language, like a > struct foo { > const int x, y; > }; > in one translation unit being compatible with > struct bar { > int x, y; > };
While I agree with the general thrust of what you are saying, I think const-ness or the lack thereof is he only tip of the iceberg here. Assigning from a pointer-to-struct-foo to a pointer-to-struct-bar will get a (well-deserved) warning even their members were declared identically. The thing that freaks me out is that the following code compiles with only that same warning about incompatible pointers. Since we are used to seeing this warning so much of the time, we might even ignore the warning. Until we actually try to RUN the program, that is. #include <stdio.h> struct foo { int x,y; }; struct bar { int x, y, z; }; int main(void) { struct bar *barptr; struct foo a = { 1, 2}; barptr = &a; barptr->z = 3; // watch out! printf("%d,%d,%d\n", barptr->x, barptr->y, barptr->z); return 0; } Sent via Deja.com http://www.deja.com/
|
Tue, 17 Jun 2003 10:31:22 GMT |
|
 |
Kaz Kylhe #7 / 14
|
 const char ** incompatible with char ** (and vice versa)?
Quote: >(BTW, Kaz, your lines are getting a bit long even after just one >quoting, so I've reflowed the paragraph above.)
Hmm, maybe I should format narrower in anticipation of heavy quoting. ;) I stick to the 79 rule quite literally, which doesn't leave any padding. Time to add a :set wrapmargin=8 to my vimrc. :)
|
Tue, 17 Jun 2003 14:55:07 GMT |
|
 |
naisb.. #8 / 14
|
 const char ** incompatible with char ** (and vice versa)?
Quote:
>>(BTW, Kaz, your lines are getting a bit long even after just one >>quoting, so I've reflowed the paragraph above.) > Hmm, maybe I should format narrower in anticipation of heavy quoting. > ;) I stick to the 79 rule quite literally, which doesn't leave any > padding. Time to add a :set wrapmargin=8 to my vimrc. :)
If you're using vim, be warned that textwidth (tw) is preferable, as wrapmargin is relative to the current window size, while tw is absolute. :set tw=72 will keep your message the same width even if you resize your terminal. --
|
Wed, 18 Jun 2003 15:11:02 GMT |
|
 |
naisb.. #9 / 14
|
 const char ** incompatible with char ** (and vice versa)?
Quote:
> The thing that freaks me out is that the following code compiles with > only that same warning about incompatible pointers. Since we are used > to seeing this warning so much of the time, we might even ignore the > warning. Until we actually try to RUN the program, that is.
You'd ignore an "assignment from incompatible pointer type" warning? I use gcc -Werror to build my projects, so I don't have that option :-) --
|
Wed, 18 Jun 2003 17:00:34 GMT |
|
 |
Kaz Kylhe #10 / 14
|
 const char ** incompatible with char ** (and vice versa)?
On Fri, 29 Dec 2000 02:31:22 GMT, Jeff Robertson Quote:
>The thing that freaks me out is that the following code compiles with >only that same warning about incompatible pointers.
That warning flags an ANSI C constraint rule violation. The C standard makes no distinction between a warning and error; it uses the term ``diagnostic''. There is no difference in emphasis among any of the diagnosable errors: an assignment between incompatible pointers is equal in stature to a missing semicolon after a declaration or unbalanced parentheses in an expression. All of these require a diagnostic, and do not require successful translation and execution. Do not ignore warnings; some of them could be required ANSI C diagnostics! Quote: >Since we are used >to seeing this warning so much of the time, we might even ignore the >warning.
Much of the time? Ouch!
|
Wed, 18 Jun 2003 18:33:25 GMT |
|
 |
Ben Pfaf #11 / 14
|
 const char ** incompatible with char ** (and vice versa)?
Quote:
> I use gcc -Werror to build my projects, so I don't have that option
Yeah, me too. Otherwise I find that I miss warnings because I fix an error in one or two files and re-`make' and the files that just had warnings don't get recompiled, so I don't get to fix those warnings. (Another item to add to the style guide.) -- "You call this a *C* question? What the hell are you smoking?" --Kaz
|
Wed, 18 Jun 2003 23:10:25 GMT |
|
 |
mike burrel #12 / 14
|
 const char ** incompatible with char ** (and vice versa)?
Quote:
>> I use gcc -Werror to build my projects, so I don't have that option > Yeah, me too. Otherwise I find that I miss warnings because I > fix an error in one or two files and re-`make' and the files that > just had warnings don't get recompiled, so I don't get to fix > those warnings.
i used to do this but point that once i turned up the warning level some, it became completely unreasonable. for example, i have used to have -Wpointer-arith turned on; the problem is that some of the glibc 2.1 header files (dealing with strings i believe) used pointer arithmetic on void pointers. also, i've found that gcc gives spurious warnings wrt setjmp()/vfork() clobberings. -- /"\ m i k e b u r r e l l
X AGAINST HTML MAIL,
|
Wed, 18 Jun 2003 23:26:38 GMT |
|
 |
Ben Pfaf #13 / 14
|
 const char ** incompatible with char ** (and vice versa)?
Quote:
> >> I use gcc -Werror to build my projects, so I don't have that option > > Yeah, me too. Otherwise I find that I miss warnings because I > > fix an error in one or two files and re-`make' and the files that > > just had warnings don't get recompiled, so I don't get to fix > > those warnings. > i used to do this but point that once i turned up the warning level some, it > became completely unreasonable. for example, i have used to have > -Wpointer-arith turned on; the problem is that some of the glibc 2.1 header > files (dealing with strings i believe) used pointer arithmetic on void > pointers. also, i've found that gcc gives spurious warnings wrt > setjmp()/vfork() clobberings.
If I had problems like that I'd probably just turn it off for the affected files and submit a patch against glibc2.1. -- Just another C hacker.
|
Wed, 18 Jun 2003 23:33:21 GMT |
|
 |
Horst Kraem #14 / 14
|
 const char ** incompatible with char ** (and vice versa)?
Quote: > I don't think that making const T ** and T ** compatible is a > great idea either. I think that making the latter acceptable for > assignment to the former is a better idea. Such a change could > be made just by modifying the constraint in C99 section 6.15.16.1 > "Simple assignment" from > -- both operands are pointers to qualified or unqualified > versions of compatible types, and the type pointed to > by the left has all the qualifiers of the type pointed > to by the right; > to a more general test, and similarly for the next constraint in > the list. > This change wouldn't open up the hole that you pointed out above > AFAICS.
I think that this *would* open up a whole because it would allow to change a const object without diagnostic: void unsafe(void) { const int ci = 42; int i = 0; int* pi = &i; int** ppi = π int const ** ppci = ppi; /* ??? safe ??? */ *ppci = &ci ; /* eqivalent to pi=&ic */ *pi = 0; /* equivalent to ci=0 , oops... */ Quote: }
Regards Horst
|
Thu, 19 Jun 2003 19:26:31 GMT |
|
|
|