Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Incompatible pointer type question

20 views
Skip to first unread message

Victor Bazarov

unread,
Jan 6, 2000, 3:00:00 AM1/6/00
to
Hi All,

Here is the code (compiled with egcs 2.91.66, Linux 2.2.13):
8<=========================================================
void foo(const char **a)
{
}

void bar(char * const *a)
{
}

void foobar(const char *a)
{
}

int main()
{
char **b = 0;
foo(b); /* causes warning 'incompatible pointer type' */
bar(b); /* no warning */
foobar(*b); /* no warning */
return 0;
}
8<=========================================================

What is the meaning of the warning and why can the way I pass the
pointer be dangerous (that's the reason for the warning, right?) ?

Thanks

Victor
--
Please remove capital A's from my address when replying by mail

Kaz Kylheku

unread,
Jan 7, 2000, 3:00:00 AM1/7/00
to
On Thu, 6 Jan 2000 17:01:40 -0800, Victor Bazarov <vAba...@dAnai.com> wrote:
>Hi All,
>
>Here is the code (compiled with egcs 2.91.66, Linux 2.2.13):
>8<=========================================================
>void foo(const char **a)
>{
>}
>
>void bar(char * const *a)
>{
>}
>
>void foobar(const char *a)
>{
>}
>
>int main()
>{
> char **b = 0;
> foo(b); /* causes warning 'incompatible pointer type' */
> bar(b); /* no warning */
> foobar(*b); /* no warning */
> return 0;
>}

This is correct behavior. It is possible to implicitly convert a
T * to const T *. The latter two function calls do exactly this.

The first one calls for a conversion from T ** to const T ** which
is a constraint violation.

>8<=========================================================
>
>What is the meaning of the warning and why can the way I pass the
>pointer be dangerous (that's the reason for the warning, right?) ?

The warning is required by ANSI C because your program has a constraint
violation. Some compilers might not be so kind and will reject your
program entirely.

The types char * and const char * are incompatible. Therefore pointers to them
are incompatible and may not be implicitly converted. Those are the rules of
the language.

mike burrell

unread,
Jan 7, 2000, 3:00:00 AM1/7/00
to
Kaz Kylheku <k...@ashi.footprints.net> wrote:
> On Thu, 6 Jan 2000 17:01:40 -0800, Victor Bazarov <vAba...@dAnai.com> wrote:
*snip*

>>void foo(const char **a)
>>{
>>}
>>
>>void bar(char * const *a)
>>{
>>}
>>
>>void foobar(const char *a)
>>{
>>}
>>
>>int main()
>>{
>> char **b = 0;
>> foo(b); /* causes warning 'incompatible pointer type' */
>> bar(b); /* no warning */
>> foobar(*b); /* no warning */
>> return 0;
>>}

> This is correct behavior. It is possible to implicitly convert a
> T * to const T *. The latter two function calls do exactly this.

> The first one calls for a conversion from T ** to const T ** which
> is a constraint violation.

*snip*

> The types char * and const char * are incompatible. Therefore pointers to them
> are incompatible and may not be implicitly converted. Those are the rules of
> the language.

that doesn't answer it for me.

first of all, i don't think the second one is converting T * to const T *
like you said. to me it reads "convert from a pointer to a pointer to a
character into a constant pointer to a (non-constant) pointer to a
character". anyway, this one is good with me if i've decoded it right.

the third one converts a T * to a const T *, which you say is okay, and then
go on to say isn't okay (though apparently it is). this one is good with me
too.

but the first one doesn't make sense to me. why is it okay to implicitly
convert from T * to const T *, but not T ** to const T **? shouldn't the
same rules apply for both? you're just converting a 'pointer to a pointer
to a character' into a 'pointer to a pointer to a constant character',
right? why is it a violation to add constness?

--
/"\ m i k e b u r r e l l
\ / ASCII RIBBON CAMPAIGN mik...@home.com
X AGAINST HTML MAIL http://mikpos.dyndns.org
/ \

Richard Stamp

unread,
Jan 7, 2000, 3:00:00 AM1/7/00
to
mike burrell <mik...@home.com> wrote in message
news:vPdd4.301$uO.2...@news1.sshe1.sk.home.com...

> first of all, i don't think the second one is converting T * to const T *
> like you said. to me it reads "convert from a pointer to a pointer to a
> character into a constant pointer to a (non-constant) pointer to a
> character". anyway, this one is good with me if i've decoded it right.

Nope. The function was

> >>void bar(char * const *a)
> >>{
> >>}

So the question is exactly what 'char *const *a' gives us. Reading this
inside-out we see that 'a' is a pointer to 'char *const', i.e. "pointer to
constant pointer to modifiable char".

If you're not very good at the "inside-out" technique, another way of
looking at this is to apply the "declaration mimics use" rule, and say that
'*a' is being declared as a 'char *const' -- which of course gives us the
same result.

This is all still OK though, because if we let T stand for "pointer to
modifiable char", we are trying to convert "pointer to T" into "pointer to
const T". This is permissible, as Kaz observed.

> the third one converts a T * to a const T *, which you say is okay, and
> then
> go on to say isn't okay (though apparently it is).

It is OK. If you look carefully I think you'll see that the one Kaz is
saying isn't OK has an extra level of indirection.

> but the first one doesn't make sense to me. why is it okay to implicitly
> convert from T * to const T *, but not T ** to const T **? shouldn't the
> same rules apply for both? you're just converting a 'pointer to a pointer
> to a character' into a 'pointer to a pointer to a constant character',
> right? why is it a violation to add constness?

The rule that the pointed-to type can "gain" a const qualification isn't
applied recursively. I don't know of a compelling reason for this, but it
is what the Standard says so compilers have to abide by it.

This is FAQ 11.10, but the FAQ doesn't say much more than I have.

Cheers
Richard
--
The FAQ is at http://www.eskimo.com/~scs/C-faq/top.html
New users try http://www.sin.khk.be/~emulov/c.l.c/welcome_to_clc.htm


Richard Stamp

unread,
Jan 7, 2000, 3:00:00 AM1/7/00
to
Kaz Kylheku <k...@ashi.footprints.net> wrote in message
news:slrn87ao5...@ashi.FootPrints.net...

> The types char * and const char * are incompatible. Therefore pointers to
> them are incompatible and may not be implicitly converted. Those are the
> rules of the language.

I don't think it's quite as simple as their being incompatible. 'char' and
'const char' are also incompatible (C90 6.5.3) but it's legal to assign
'char *' to 'const char *' (though not the other way around).

Lawrence Kirby

unread,
Jan 8, 2000, 3:00:00 AM1/8/00
to
In article <vPdd4.301$uO.2...@news1.sshe1.sk.home.com>
mik...@home.com "mike burrell" writes:

>Kaz Kylheku <k...@ashi.footprints.net> wrote:
>> On Thu, 6 Jan 2000 17:01:40 -0800, Victor Bazarov <vAba...@dAnai.com> wrote:>*snip*
>>>void foo(const char **a)
>>>{
>>>}
>>>

>>>void bar(char * const *a)
>>>{
>>>}
>>>

>>>void foobar(const char *a)
>>>{
>>>}
>>>
>>>int main()
>>>{
>>> char **b = 0;
>>> foo(b); /* causes warning 'incompatible pointer type' */
>>> bar(b); /* no warning */
>>> foobar(*b); /* no warning */
>>> return 0;
>>>}
>
>> This is correct behavior. It is possible to implicitly convert a
>> T * to const T *. The latter two function calls do exactly this.
>
>> The first one calls for a conversion from T ** to const T ** which
>> is a constraint violation.
>
>*snip*
>

>> The types char * and const char * are incompatible. Therefore pointers to them>> are incompatible and may not be implicitly converted. Those are the rules of
>> the language.
>

>that doesn't answer it for me.
>

>first of all, i don't think the second one is converting T * to const T *
>like you said. to me it reads "convert from a pointer to a pointer to a
>character into a constant pointer to a (non-constant) pointer to a
>character". anyway, this one is good with me if i've decoded it right.
>

>the third one converts a T * to a const T *, which you say is okay, and then

>go on to say isn't okay (though apparently it is). this one is good with me
>too.


>
>but the first one doesn't make sense to me. why is it okay to implicitly
>convert from T * to const T *, but not T ** to const T **? shouldn't the
>same rules apply for both? you're just converting a 'pointer to a pointer
>to a character' into a 'pointer to a pointer to a constant character',
>right? why is it a violation to add constness?

You have to consider what "constness" is. const doesn't add functionality
to the C language: if you added an option to a compiler that made
it simply ignore all instances of const it encountered then then behaviour
of a program that was correct to start with would be unchanged.
const is a safety feature: it propagates via the type system so that
a program that defines something as const can't later attempt
to modify it without the compiler generating a warning somewhere down
the line. You can override this using casts (and library functions
like strstr() have an implied cast in their implementation). Similarly
if you have a pointer to a const type you can't modify what the
pointer points at unless you use a cast.

So what would be the problem if C allowed a T ** pointer to be
converted implicitly to const T**? Consider the sequence:

const int ci = 42;
const int *pci = &ci;
int *pi;
int **ppi = &pi;
const int **ppci = ppi; /* The critical step that C forbids, ppci now
points to pi */
*ppci = pci; /* pi now points to ci */
*pi = 43;

And we've now managed an attempt to modify ci, i.e. a const object, without
using a cast. If this sort of this was possible then the const
mechanism would affort little protection, so C only permits qualifiers
to be added implicitly in pointer assignment at the directly pointed at
type.

--
-----------------------------------------
Lawrence Kirby | fr...@genesis.demon.co.uk
Wilts, England | 7073...@compuserve.com
-----------------------------------------


Steve Summit

unread,
Jan 14, 2000, 3:00:00 AM1/14/00
to
In article <85580k$he5$1...@soap.pipex.net>, Richard Stamp

<richar...@acm.org> writes:
>mike burrell <mik...@home.com> wrote in message
>news:vPdd4.301$uO.2...@news1.sshe1.sk.home.com...
>> ...but the first one doesn't make sense to me. why is it okay to

>> implicitly convert from T * to const T *, but not T ** to const T **?
>> shouldn't the same rules apply for both? you're just converting a
>> 'pointer to a pointer to a character' into a 'pointer to a pointer to
>> a constant character', right? why is it a violation to add constness?
>
> The rule that the pointed-to type can "gain" a const qualification isn't
> applied recursively. I don't know of a compelling reason for this, but it
> is what the Standard says so compilers have to abide by it.
>
> This is FAQ 11.10, but the FAQ doesn't say much more than I have.

The only person I know who can make sense of this stuff
(i.e. distill the discussions on this topic in comp.std.c
down to language which mere mortals can understand) is Tanmoy
Bhattacharya. Here's the book's version of question 11.10,
much of which basically paraphrases a message Tanmoy once sent me:

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.
(const char ** is pointer-to-pointer-to-const-char, and the
exception therefore does not apply.)

The reason that you cannot assign a char ** value to a
const char ** pointer is somewhat obscure. Given that the
const qualifier exists at all, the compiler would like to help
you keep your promises not to modify const values. That's why
you can assign a char * to a const char *, but not the other
way around: it's clearly safe to "add" const-ness to a simple
pointer, but it would be dangerous to take it away. However,
suppose you performed the following more complicated series of
assignments:

const char c = 'x'; /* 1 */
char *p1; /* 2 */
const char **p2 = &p1; /* 3 */
*p2 = &c; /* 4 */
*p1 = 'X'; /* 5 */

In line 3, we assign a char ** to a const char **. (The
compiler should complain.) In line 4, we assign a const char *
to a const char *; this is clearly legal. In line 5, we modify
what a char * points to -- this is supposed to be legal.
However, p1 ends up pointing to c, which is const. This came
about in line 4, because *p2 was really p1. This was set up
in line 3, which is an assignment of a form that is disallowed,
and this is exactly *why* line 3 is disallowed.

Assigning a char ** to a const char ** (as in line 3, and in the
original question) is not immediately dangerous. But it sets up
a situation in which p2's promise -- that the ultimately-pointed-
to value won't be modified -- cannot be kept.

(C++ has more complicated rules for assigning const-qualified
pointers which let you make more kinds of assignments without
incurring warnings, but still protect against inadvertent
attempts to modify const values. C++ would still not allow
assigning a char ** to a const char **, but it would let you
get away with assigning a char ** to a const char * const *.)

In C, 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: ANSI Sec. 3.1.2.6, Sec. 3.3.16.1, Sec. 3.5.3
ISO Sec. 6.1.2.6, Sec. 6.3.16.1, Sec. 6.5.3
H&S Sec. 7.9.1 pp. 221-2

Steve Summit
s...@eskimo.com
--
Programming Challenge #2: Avoid Pointless Exceptions.
See http://www.eskimo.com/~scs/challenge/.

0 new messages