Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C Programming > Pointer / Const Warnings

Reply
Thread Tools

Pointer / Const Warnings

 
 
Christopher Key
Guest
Posts: n/a
 
      09-11-2007
Hello,

Can anyone suggest why gcc (-W -Wall) complains,

test.c:22: warning: passing arg 1 of `f' from incompatible pointer type

when compiling the following code? Change the declation of f to,

void f(int *const *const p, int *const *const q) {

prevents the warning, but I just can't see why this should be.


Many thanks,

Chris



#include <stdlib.h>
#include <stdio.h>

void f(const int *const *const p, int *const *const q);

int main() {

int **p, **q, i;

p = calloc(2, sizeof(int *));
q = calloc(2, sizeof(int *));
for (i=0; i<2; i++) {
p[i] = calloc(2, sizeof(int));
q[i] = calloc(2, sizeof(int));
}

p[0][0] = 41;
p[0][1] = 40;
p[1][0] = 39;
p[1][1] = 38;

f(p, q);

fprintf(stderr, "%d %d %d %d", q[0][0], q[0][1], q[1][0], q[1][1]);

for (i=0; i<2; i++) {
free(p[i]);
free(q[i]);
}
free(p);
free(q);

return 0;
}

void f(const int *const *const p, int *const *const q) {
q[0][0] = p[0][0] + 1;
q[0][1] = p[0][1] + 2;
q[1][0] = p[1][0] + 3;
q[1][1] = p[1][1] + 4;
}
 
Reply With Quote
 
 
 
 
user923005
Guest
Posts: n/a
 
      09-11-2007
On Sep 11, 10:23 am, Christopher Key <(E-Mail Removed)> wrote:
> Hello,
>
> Can anyone suggest why gcc (-W -Wall) complains,
>
> test.c:22: warning: passing arg 1 of `f' from incompatible pointer type
>
> when compiling the following code? Change the declation of f to,
>
> void f(int *const *const p, int *const *const q) {
>
> prevents the warning, but I just can't see why this should be.
>
> Many thanks,
>
> Chris
>
> #include <stdlib.h>
> #include <stdio.h>
>
> void f(const int *const *const p, int *const *const q);
>
> int main() {
>
> int **p, **q, i;
>
> p = calloc(2, sizeof(int *));
> q = calloc(2, sizeof(int *));
> for (i=0; i<2; i++) {
> p[i] = calloc(2, sizeof(int));
> q[i] = calloc(2, sizeof(int));
> }
>
> p[0][0] = 41;
> p[0][1] = 40;
> p[1][0] = 39;
> p[1][1] = 38;
>
> f(p, q);
>
> fprintf(stderr, "%d %d %d %d", q[0][0], q[0][1], q[1][0], q[1][1]);
>
> for (i=0; i<2; i++) {
> free(p[i]);
> free(q[i]);
> }
> free(p);
> free(q);
>
> return 0;
>
> }
>
> void f(const int *const *const p, int *const *const q) {
> q[0][0] = p[0][0] + 1;
> q[0][1] = p[0][1] + 2;
> q[1][0] = p[1][0] + 3;
> q[1][1] = p[1][1] + 4;
>
>
>
> }


I agree with you. I don't think that the compiler should ever
complain about a *decrease* in pointer capability. I think GCC is
blowing pickle smoke and horse-feathers here. I suppose you could
cast it like this:

#include <stdlib.h>
#include <stdio.h>

static void douglass_adams(const int *const * const p, int *const *
const q);

int main()
{
int **p,
**q,
i;

p = calloc(2, sizeof(int *));
q = calloc(2, sizeof(int *));
if (p && q) {
for (i = 0; i < 2; i++) {
p[i] = calloc(2, sizeof(int));
q[i] = calloc(2, sizeof(int));
if (p[i] == NULL || q[i] == NULL) {
printf("Memory allocation error at %s:%d.\n",
__FILE__, __LINE__);
exit(EXIT_FAILURE);
}
}

p[0][0] = 41;
p[0][1] = 40;
p[1][0] = 39;
p[1][1] = 38;

douglass_adams((const int *const * const) p, q);

fprintf(stderr, "%d %d %d %d", q[0][0], q[0][1], q[1][0], q[1]
[1]);

for (i = 0; i < 2; i++) {
free(p[i]);
free(q[i]);
}
free(p);
free(q);
}
return 0;
}


static void douglass_adams(const int *const * const p, int *const *
const q)
{
q[0][0] = p[0][0] + 1;
q[0][1] = p[0][1] + 2;
q[1][0] = p[1][0] + 3;
q[1][1] = p[1][1] + 4;
}



 
Reply With Quote
 
 
 
 
christian.bau
Guest
Posts: n/a
 
      09-11-2007
The warning is confusing, but completely legitimate. The reason is a
bit more complicated.

Lets say I have a pointer variable int* x and another pointer variable
const int* y. Clearly I shouldn't be able to just assign y to x
without an explicit cast, right? So an assignment x = y should be
illegal.

Now lets say I have two other pointer variables int* *p and const int*
*q. The assignment *p = y is illegal, just as x = y was illegal, but
*q = y is legal.

And now I write p = &x; q = p; . The last assignment is what the
compiler complained about. And now you can see why: Once you have done
this, you can write *q = y; which assigns y to x which we didn't want
to allow. So the following sequence of instructions would assign to a
const int value without any casts:

const int i = 10;
const int* y = &i;
int* x;
int* *p = &x;
const int* *q = p; // Warning
*p = y;
*x = 20;


 
Reply With Quote
 
user923005
Guest
Posts: n/a
 
      09-11-2007
On Sep 11, 12:50 pm, "christian.bau"
<(E-Mail Removed)> wrote:
> The warning is confusing, but completely legitimate. The reason is a
> bit more complicated.
>
> Lets say I have a pointer variable int* x and another pointer variable
> const int* y. Clearly I shouldn't be able to just assign y to x
> without an explicit cast, right? So an assignment x = y should be
> illegal.


Right, but in this case we have a decrease in pointer capability. So
(to me) it is more analogous to:

int main(void)
{
int a;
int *x = &a;
const int *y;
y = x; /* Totally harmless, of course */
return 0;
}


> Now lets say I have two other pointer variables int* *p and const int*
> *q. The assignment *p = y is illegal, just as x = y was illegal, but
> *q = y is legal.
>
> And now I write p = &x; q = p; . The last assignment is what the
> compiler complained about. And now you can see why: Once you have done
> this, you can write *q = y; which assigns y to x which we didn't want
> to allow. So the following sequence of instructions would assign to a
> const int value without any casts:
>
> const int i = 10;
> const int* y = &i;
> int* x;
> int* *p = &x;
> const int* *q = p; // Warning
> *p = y;
> *x = 20;


In this case:

int main(void)
{
const int i = 10;
const int *y = &i;
int *x;
int **p = &x;
const int **q = p; /* Warning? I see no danger from it.
We just have a less capable pointer to pointer to i in q. */
*p = y;
*x = 20;
return 0;
}

I think I am going to need some help to understand what could possibly
be bad about this assignment.

 
Reply With Quote
 
christian.bau
Guest
Posts: n/a
 
      09-11-2007
On Sep 11, 9:04 pm, user923005 <(E-Mail Removed)> wrote:
> On Sep 11, 12:50 pm, "christian.bau"
>
> <(E-Mail Removed)> wrote:
> > The warning is confusing, but completely legitimate. The reason is a
> > bit more complicated.

>
> > Lets say I have a pointer variable int* x and another pointer variable
> > const int* y. Clearly I shouldn't be able to just assign y to x
> > without an explicit cast, right? So an assignment x = y should be
> > illegal.

>
> Right, but in this case we have a decrease in pointer capability. So
> (to me) it is more analogous to:
>
> int main(void)
> {
> int a;
> int *x = &a;
> const int *y;
> y = x; /* Totally harmless, of course */
> return 0;
>
>
>
> }
> > Now lets say I have two other pointer variables int* *p and const int*
> > *q. The assignment *p = y is illegal, just as x = y was illegal, but
> > *q = y is legal.

>
> > And now I write p = &x; q = p; . The last assignment is what the
> > compiler complained about. And now you can see why: Once you have done
> > this, you can write *q = y; which assigns y to x which we didn't want
> > to allow. So the following sequence of instructions would assign to a
> > const int value without any casts:

>
> > const int i = 10;
> > const int* y = &i;
> > int* x;
> > int* *p = &x;
> > const int* *q = p; // Warning
> > *p = y;
> > *x = 20;

>
> In this case:
>
> int main(void)
> {
> const int i = 10;
> const int *y = &i;
> int *x;
> int **p = &x;
> const int **q = p; /* Warning? I see no danger from it.
> We just have a less capable pointer to pointer to i in q. */
> *p = y;
> *x = 20;
> return 0;
>
> }
>
> I think I am going to need some help to understand what could possibly
> be bad about this assignment.


Step by step:

1: const int i = 10;
2: const int* y = &i;
3: int* x;
4: int* *p = &x;
5: const int* *q = p; // Warning
6: *q = y; // Oops, I got that wrong in my first post. *q, not *p.
7: *x = 20;

1. A const int is initialised with a value of 10. Can't be changed
anymore.
2. y points to i. You can't modify i using y.
3. x is uninitialised.
4. p points to x.
5. q also points to x. Next statement shows why this is dangerous.
6. This assigns y to x because q points to x. That is it assigns a
const int* to an int* which shouldn't happen. (My original post got
that wrong. The assignment *p = y also assigned y to x, but here the
compiler would have noticed).
7. Really bad; this assigns 20 to i.

So the problem with the assignment int** to const int** is that in the
next statement, a const int* can be assigned to an int* without the
compiler noticing and complaining. You see the obvious loss of
capability which is safe: When assigning an int** to a const int**,
you lose the capability of assigning an int value with double
indirection. But you also gain a dangerous and unsafe capability: You
gain the capability of storing a const int* in a location that is only
allowed to hold an int*.

While *q is less capable than *p (*p allows you to store integers
while *q only lets you read integers), q is actually more capable than
p, because q allows you to store both int* and const int*, while q
only allows you to store int*.


 
Reply With Quote
 
user923005
Guest
Posts: n/a
 
      09-12-2007
On Sep 11, 3:58 pm, "christian.bau" <(E-Mail Removed)>
wrote:
> On Sep 11, 9:04 pm, user923005 <(E-Mail Removed)> wrote:
>
>
>
>
>
> > On Sep 11, 12:50 pm, "christian.bau"

>
> > <(E-Mail Removed)> wrote:
> > > The warning is confusing, but completely legitimate. The reason is a
> > > bit more complicated.

>
> > > Lets say I have a pointer variable int* x and another pointer variable
> > > const int* y. Clearly I shouldn't be able to just assign y to x
> > > without an explicit cast, right? So an assignment x = y should be
> > > illegal.

>
> > Right, but in this case we have a decrease in pointer capability. So
> > (to me) it is more analogous to:

>
> > int main(void)
> > {
> > int a;
> > int *x = &a;
> > const int *y;
> > y = x; /* Totally harmless, of course */
> > return 0;

>
> > }
> > > Now lets say I have two other pointer variables int* *p and const int*
> > > *q. The assignment *p = y is illegal, just as x = y was illegal, but
> > > *q = y is legal.

>
> > > And now I write p = &x; q = p; . The last assignment is what the
> > > compiler complained about. And now you can see why: Once you have done
> > > this, you can write *q = y; which assigns y to x which we didn't want
> > > to allow. So the following sequence of instructions would assign to a
> > > const int value without any casts:

>
> > > const int i = 10;
> > > const int* y = &i;
> > > int* x;
> > > int* *p = &x;
> > > const int* *q = p; // Warning
> > > *p = y;
> > > *x = 20;

>
> > In this case:

>
> > int main(void)
> > {
> > const int i = 10;
> > const int *y = &i;
> > int *x;
> > int **p = &x;
> > const int **q = p; /* Warning? I see no danger from it.
> > We just have a less capable pointer to pointer to i in q. */
> > *p = y;
> > *x = 20;
> > return 0;

>
> > }

>
> > I think I am going to need some help to understand what could possibly
> > be bad about this assignment.

>
> Step by step:
>
> 1: const int i = 10;
> 2: const int* y = &i;
> 3: int* x;
> 4: int* *p = &x;
> 5: const int* *q = p; // Warning
> 6: *q = y; // Oops, I got that wrong in my first post. *q, not *p.
> 7: *x = 20;
>
> 1. A const int is initialised with a value of 10. Can't be changed
> anymore.
> 2. y points to i. You can't modify i using y.
> 3. x is uninitialised.
> 4. p points to x.
> 5. q also points to x. Next statement shows why this is dangerous.
> 6. This assigns y to x because q points to x. That is it assigns a
> const int* to an int* which shouldn't happen. (My original post got
> that wrong. The assignment *p = y also assigned y to x, but here the
> compiler would have noticed).
> 7. Really bad; this assigns 20 to i.
>
> So the problem with the assignment int** to const int** is that in the
> next statement, a const int* can be assigned to an int* without the
> compiler noticing and complaining. You see the obvious loss of
> capability which is safe: When assigning an int** to a const int**,
> you lose the capability of assigning an int value with double
> indirection. But you also gain a dangerous and unsafe capability: You
> gain the capability of storing a const int* in a location that is only
> allowed to hold an int*.
>
> While *q is less capable than *p (*p allows you to store integers
> while *q only lets you read integers), q is actually more capable than
> p, because q allows you to store both int* and const int*, while q
> only allows you to store int*.


OK, now I get it. That could cause subtle problems and it is good to
understand it.
Thanks for the detailed explanation.

 
Reply With Quote
 
Christopher Key
Guest
Posts: n/a
 
      09-12-2007
christian.bau wrote:
> Step by step:
>
> 1: const int i = 10;
> 2: const int* y = &i;
> 3: int* x;
> 4: int* *p = &x;
> 5: const int* *q = p; // Warning
> 6: *q = y; // Oops, I got that wrong in my first post. *q, not *p.
> 7: *x = 20;
>
> 1. A const int is initialised with a value of 10. Can't be changed
> anymore.
> 2. y points to i. You can't modify i using y.
> 3. x is uninitialised.
> 4. p points to x.
> 5. q also points to x. Next statement shows why this is dangerous.
> 6. This assigns y to x because q points to x. That is it assigns a
> const int* to an int* which shouldn't happen. (My original post got
> that wrong. The assignment *p = y also assigned y to x, but here the
> compiler would have noticed).
> 7. Really bad; this assigns 20 to i.
>
> So the problem with the assignment int** to const int** is that in the
> next statement, a const int* can be assigned to an int* without the
> compiler noticing and complaining. You see the obvious loss of
> capability which is safe: When assigning an int** to a const int**,
> you lose the capability of assigning an int value with double
> indirection. But you also gain a dangerous and unsafe capability: You
> gain the capability of storing a const int* in a location that is only
> allowed to hold an int*.
>
> While *q is less capable than *p (*p allows you to store integers
> while *q only lets you read integers), q is actually more capable than
> p, because q allows you to store both int* and const int*, while q
> only allows you to store int*.
>
>


Thanks very much Christian, I'd come across this issue in a FAQ
recently, but I now understand it a lot better. I'm still not entirely
sure why the compiler is complaining though, as I'm not assigning 'const
int **q = p', but rather 'const int *const *const q = p', which would
then make 6 impossible. As a simpler example, take the following, for
which gcc still warns:


#include <stdlib.h>
#include <stdio.h>

void f(const int *const *const p);

int main() {
int x = 10;
int *y = &x;
int **p = &y;

f(p);

return 0;
}

void f(const int *const *const p) {
/* What can I do here that's unsafe??? */
fprintf(stdout, "p is %d", **p);
}


Regards,

Chris
 
Reply With Quote
 
christian.bau
Guest
Posts: n/a
 
      09-12-2007
On Sep 12, 9:15 am, Christopher Key <(E-Mail Removed)> wrote:
> christian.bau wrote:
> > Step by step:

>
> > 1: const int i = 10;
> > 2: const int* y = &i;
> > 3: int* x;
> > 4: int* *p = &x;
> > 5: const int* *q = p; // Warning
> > 6: *q = y; // Oops, I got that wrong in my first post. *q, not *p.
> > 7: *x = 20;

>
> > 1. A const int is initialised with a value of 10. Can't be changed
> > anymore.
> > 2. y points to i. You can't modify i using y.
> > 3. x is uninitialised.
> > 4. p points to x.
> > 5. q also points to x. Next statement shows why this is dangerous.
> > 6. This assigns y to x because q points to x. That is it assigns a
> > const int* to an int* which shouldn't happen. (My original post got
> > that wrong. The assignment *p = y also assigned y to x, but here the
> > compiler would have noticed).
> > 7. Really bad; this assigns 20 to i.

>
> > So the problem with the assignment int** to const int** is that in the
> > next statement, a const int* can be assigned to an int* without the
> > compiler noticing and complaining. You see the obvious loss of
> > capability which is safe: When assigning an int** to a const int**,
> > you lose the capability of assigning an int value with double
> > indirection. But you also gain a dangerous and unsafe capability: You
> > gain the capability of storing a const int* in a location that is only
> > allowed to hold an int*.

>
> > While *q is less capable than *p (*p allows you to store integers
> > while *q only lets you read integers), q is actually more capable than
> > p, because q allows you to store both int* and const int*, while q
> > only allows you to store int*.

>
> Thanks very much Christian, I'd come across this issue in a FAQ
> recently, but I now understand it a lot better. I'm still not entirely
> sure why the compiler is complaining though, as I'm not assigning 'const
> int **q = p', but rather 'const int *const *const q = p', which would
> then make 6 impossible. As a simpler example, take the following, for
> which gcc still warns:
>
> #include <stdlib.h>
> #include <stdio.h>
>
> void f(const int *const *const p);
>
> int main() {
> int x = 10;
> int *y = &x;
> int **p = &y;
>
> f(p);
>
> return 0;
>
> }
>
> void f(const int *const *const p) {
> /* What can I do here that's unsafe??? */
> fprintf(stdout, "p is %d", **p);
>
> }


This is gcc. gcc has a tendency to produce warnings in some cases that
should be hard errors. Normally, a compiler should give warnings in
situations where the compiler writer thinks you might have done
something that you didn't intend to do, even though they are legal C,
but gcc also gives mere warnings in cases that are errors. In the
first case, the compiler writer _should_ be careful only to give a
warning if damage is actually possible, in the case of errors the
language determines whether there is an error or not, and the compiler
doesn't have much choice. This kind of assignment is I think a real
error, so even though nothing can happen in this particular case,
there should be an error message.

Yes, the middle "const" makes it less likely that an error would
occur. It is still possible. See this example:

void change_if_not_const (const int* p, int ismodifiable, int
newvalue) {
if (ismodifiable)
* (int *) p = newvalue;
}

int x;
const int y;

change_if_not_const (&x, 1, 100);
change_if_not_const (&y, 0, 100);

The function is quite reasonable, and the calls are correct and safe.
Now I write a similar function:

void change_if_not_const (const int* const* p, int ismodifiable, const
int* newvalue) {
if (ismodifiable)
* (const int* *p) = newvalue;
}

As in the first example, passing a "const int* const*" is of course
perfectly legal. Passing a "const int* *" is fine as well, but passing
an "int **" is dangerous and must not be allowed.

 
Reply With Quote
 
Ben Bacarisse
Guest
Posts: n/a
 
      09-13-2007
"christian.bau" <(E-Mail Removed)> writes:

> On Sep 12, 9:15 am, Christopher Key <(E-Mail Removed)> wrote:
>> christian.bau wrote:
>> > Step by step:

>>
>> > 1: const int i = 10;
>> > 2: const int* y = &i;
>> > 3: int* x;
>> > 4: int* *p = &x;
>> > 5: const int* *q = p; // Warning
>> > 6: *q = y; // Oops, I got that wrong in my first post. *q, not *p.
>> > 7: *x = 20;

>>
>> > 1. A const int is initialised with a value of 10. Can't be changed
>> > anymore.
>> > 2. y points to i. You can't modify i using y.
>> > 3. x is uninitialised.
>> > 4. p points to x.
>> > 5. q also points to x. Next statement shows why this is dangerous.
>> > 6. This assigns y to x because q points to x. That is it assigns a
>> > const int* to an int* which shouldn't happen. (My original post got
>> > that wrong. The assignment *p = y also assigned y to x, but here the
>> > compiler would have noticed).
>> > 7. Really bad; this assigns 20 to i.

>>
>> > So the problem with the assignment int** to const int** is that in the
>> > next statement, a const int* can be assigned to an int* without the
>> > compiler noticing and complaining. You see the obvious loss of
>> > capability which is safe: When assigning an int** to a const int**,
>> > you lose the capability of assigning an int value with double
>> > indirection. But you also gain a dangerous and unsafe capability: You
>> > gain the capability of storing a const int* in a location that is only
>> > allowed to hold an int*.

>>
>> > While *q is less capable than *p (*p allows you to store integers
>> > while *q only lets you read integers), q is actually more capable than
>> > p, because q allows you to store both int* and const int*, while q
>> > only allows you to store int*.

>>
>> Thanks very much Christian, I'd come across this issue in a FAQ
>> recently, but I now understand it a lot better. I'm still not entirely
>> sure why the compiler is complaining though, as I'm not assigning 'const
>> int **q = p', but rather 'const int *const *const q = p', which would
>> then make 6 impossible. As a simpler example, take the following, for
>> which gcc still warns:
>>
>> #include <stdlib.h>
>> #include <stdio.h>
>>
>> void f(const int *const *const p);
>>
>> int main() {
>> int x = 10;
>> int *y = &x;
>> int **p = &y;
>>
>> f(p);
>>
>> return 0;
>>
>> }
>>
>> void f(const int *const *const p) {
>> /* What can I do here that's unsafe??? */
>> fprintf(stdout, "p is %d", **p);
>>
>> }

>
> This is gcc. gcc has a tendency to produce warnings in some cases that
> should be hard errors.


I don't think that is what the OP is talking about. The code is
"safe" (f can only modify a const object by casting away const) but
the call is not allowed anyway. The OP expected the code to compile
without any diagnostic (as it wound under a C++ compiler for
example). In fact the standard insists that a diagnostic be issued.

See
http://groups.google.com/group/comp....30f3feeeda0049
for the details.

The reason is simply that the rule in the standard is simple to state
and implement. If you follow that thread to the end you will see a
remark that suggests the complexity adding of C's extra restrict
qualifier meant that no one was perpared to work though the details to
propose an alternative.

<examples snipped>
Your examples use casts so I don't think they shed any more light on
why int ** can't be passed to a int const *const *const parameter.

--
Ben.
 
Reply With Quote
 
Charlie Gordon
Guest
Posts: n/a
 
      09-13-2007
"Christopher Key" <(E-Mail Removed)> a écrit dans le message de news:
46e7a029$0$21093$(E-Mail Removed)...
>
> #include <stdlib.h>
> #include <stdio.h>
>
> void f(const int *const *const p);
>
> int main() {
> int x = 10;
> int *y = &x;
> int **p = &y;
>
> f(p);
>
> return 0;
> }
>
> void f(const int *const *const p) {
> /* What can I do here that's unsafe??? */
> fprintf(stdout, "p is %d", **p);
> }


C currently does not allow it, even though it is safe.
I think D allows it, but with a different syntax and slightly different
semantics.

--
Chqrlie.


 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
const pointer versus const content pointed to by pointer Disc Magnet C Programming 1 05-06-2010 10:27 AM
const vector<A> vs vector<const A> vs const vector<const A> Javier C++ 2 09-04-2007 08:46 PM
Casting int'** to 'const int * const * const' dosn't work, why? Jonas.Holmsten@gmail.com C Programming 11 07-01-2007 06:16 PM
Smart Pointer release() const : it can set the pointer to null with the keyword "const"? coala C++ 1 09-06-2006 03:00 PM
Smart Pointer release() const : it can set the pointer to null with the keyword "const"? coala C++ 3 09-06-2006 02:58 PM



Advertisments