Pointer / Const Warnings

Discussion in 'C Programming' started by Christopher Key, Sep 11, 2007.

  1. 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 = calloc(2, sizeof(int));
    q = 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);
    free(q);
    }
    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;
    }
    Christopher Key, Sep 11, 2007
    #1
    1. Advertising

  2. Christopher Key

    user923005 Guest

    On Sep 11, 10:23 am, Christopher Key <> 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 = calloc(2, sizeof(int));
    > q = 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);
    > free(q);
    > }
    > 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 = calloc(2, sizeof(int));
    q = calloc(2, sizeof(int));
    if (p == NULL || q == 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);
    free(q);
    }
    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;
    }
    user923005, Sep 11, 2007
    #2
    1. Advertising

  3. 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;
    christian.bau, Sep 11, 2007
    #3
  4. Christopher Key

    user923005 Guest

    On Sep 11, 12:50 pm, "christian.bau"
    <> 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.
    user923005, Sep 11, 2007
    #4
  5. On Sep 11, 9:04 pm, user923005 <> wrote:
    > On Sep 11, 12:50 pm, "christian.bau"
    >
    > <> 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*.
    christian.bau, Sep 11, 2007
    #5
  6. Christopher Key

    user923005 Guest

    On Sep 11, 3:58 pm, "christian.bau" <>
    wrote:
    > On Sep 11, 9:04 pm, user923005 <> wrote:
    >
    >
    >
    >
    >
    > > On Sep 11, 12:50 pm, "christian.bau"

    >
    > > <> 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.
    user923005, Sep 12, 2007
    #6
  7. 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
    Christopher Key, Sep 12, 2007
    #7
  8. On Sep 12, 9:15 am, Christopher Key <> 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.
    christian.bau, Sep 12, 2007
    #8
  9. "christian.bau" <> writes:

    > On Sep 12, 9:15 am, Christopher Key <> 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.lang.c/msg/fb30f3feeeda0049
    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.
    Ben Bacarisse, Sep 13, 2007
    #9
  10. "Christopher Key" <> a écrit dans le message de news:
    46e7a029$0$21093$...
    >
    > #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.
    Charlie Gordon, Sep 13, 2007
    #10
  11. Ben Bacarisse wrote:
    > "christian.bau" <> writes:
    >
    >> On Sep 12, 9:15 am, Christopher Key <> 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.lang.c/msg/fb30f3feeeda0049
    > 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.
    >


    Thanks Ben,

    I think that answers my query.

    Could you tell me what the recommended practice is in this case? What
    I'm trying to do is to pass a pointer to an array of pointers each
    pointer to an array of ints, probably best described by:

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

    although this excludes the assertion that p is const too. I'd like code
    calling f to be able to be sure that the data they passed in remains
    unchanged, and for the compiler to be able to optimise around that if
    possible.

    Regards,

    Chris
    Christopher Key, Sep 17, 2007
    #11
  12. Christopher Key <> writes:

    > Ben Bacarisse wrote:

    <on passing T ** to const T *const * parameter>
    >> 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.lang.c/msg/fb30f3feeeda0049
    >> 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.
    >>

    >
    > Thanks Ben,
    >
    > I think that answers my query.
    >
    > Could you tell me what the recommended practice is in this case?


    Not really. I suspect that style guides and personal preferences
    differ over what to do.

    > What
    > I'm trying to do is to pass a pointer to an array of pointers each
    > pointer to an array of ints, probably best described by:
    >
    > void f(const int *const p[]);
    >
    > although this excludes the assertion that p is const too.


    That is almost universally agreed to be OK leave that (first) const
    out since f can't modify p itself in any significant (non-local) way.

    > I'd like
    > code calling f to be able to be sure that the data they passed in
    > remains unchanged, and for the compiler to be able to optimise around
    > that if possible.


    I would leave the 'const's in the parameter type and accept that I
    need a cast to pass the parameter. I'd be tempted to add a wrapper to
    do this so the cast appears only once:

    void f_safe(int *p[])
    {
    f((const int **)p);
    }

    The other way round would be to change the prototype for f and put the
    cast at the top of the function, initialising a local variable (in
    which case I would add the final const):

    void f(int *dummy[])
    {
    const int *const *const p = (const int **)dummy;
    ...
    }

    Note that I have used the minimal cast required (const int **) -- the
    const "between" the stars is not needed.

    Of course, the simplest solution is just to loose the outer const
    altogether.

    --
    Ben.
    Ben Bacarisse, Sep 17, 2007
    #12
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. coala
    Replies:
    3
    Views:
    361
    coala
    Sep 6, 2006
  2. coala
    Replies:
    1
    Views:
    577
    Victor Bazarov
    Sep 6, 2006
  3. Javier
    Replies:
    2
    Views:
    543
    James Kanze
    Sep 4, 2007
  4. Disc Magnet
    Replies:
    1
    Views:
    616
    Ian Collins
    May 6, 2010
  5. Ted Sung
    Replies:
    1
    Views:
    299
    Sherm Pendley
    Aug 30, 2004
Loading...

Share This Page