• pedantic gcc and const 2D arrays

    From Michael S@already5chosen@yahoo.com to comp.lang.c on Thu Apr 9 01:21:07 2026
    From Newsgroup: comp.lang.c

    IIRC, this is my first on-topic post in comp.lang.c group.
    As they say, nobody is perfect.

    // pedant_2d.c
    int bar(const int a[5][42])
    {
    return a[0][0];
    }

    int foo(int x)
    {
    int a[5][42];
    a[0][0] = 1;
    return bar(a);
    }

    // end of pedant_2d.c


    $ gcc -std=c17 -pedantic -c pedant_2d.c
    pedant_2d.c: In function 'foo':
    pedant_2d.c:10:14: warning: invalid use of pointers to arrays with
    different qualifiers in ISO C before C23 [-Wpedantic] 10 | return
    bar(a); | ^

    What does it mean ?


    --- Synchronet 3.21f-Linux NewsLink 1.2
  • From Keith Thompson@Keith.S.Thompson+u@gmail.com to comp.lang.c on Wed Apr 8 15:57:05 2026
    From Newsgroup: comp.lang.c

    Michael S <already5chosen@yahoo.com> writes:
    IIRC, this is my first on-topic post in comp.lang.c group.
    As they say, nobody is perfect.

    // pedant_2d.c
    int bar(const int a[5][42])
    {
    return a[0][0];
    }

    int foo(int x)
    {
    int a[5][42];
    a[0][0] = 1;
    return bar(a);
    }

    // end of pedant_2d.c


    $ gcc -std=c17 -pedantic -c pedant_2d.c
    pedant_2d.c: In function 'foo':
    pedant_2d.c:10:14: warning: invalid use of pointers to arrays with
    different qualifiers in ISO C before C23 [-Wpedantic] 10 | return
    bar(a); | ^

    What does it mean ?

    This is an incomplete answer. Perhaps someone else can fill in
    some more details.

    Given the parameter declaration `const int a[5][42]`, a is of
    course a pointer, not an array. Specifically, it's of type
    `constint(*)[42])`, or "pointer to array 42 of const int".
    (The 5 is quietly ignored.)

    The argument `a` in the call (it might have been clearer to use
    distinct names) is of type `int[5][42]`, or "array 5 of array
    42 of int". In the call, as in most contexts, it decays to
    `int (*)[42]`, or "pointer to array 42 of int".

    The problem is that the types "pointer to array N of int" and
    "pointer to array N of const int" are distinct and are not assignment-compatible.

    This seems to be a case where forbidding assignment does not promote
    safety. Assigning a `const int*` value to an `int*` object, if
    it were allowed, would permit bypassing const correctness, such as
    quietly permitting an attempt to assign a value to a const object.
    But prior to C23, the rules were a bit more strict than they need
    to be.

    A simple example:

    int main(void) {
    int (*x)[10];
    const int (*y)[10];
    x = y; // discards "const"
    y = x;
    }

    The first assignment is a constraint violation in all versions of
    standard C because it discards the "const" qualifier. The second
    gets the "before C23" diagnostic with gcc. (clang doesn't
    complain about it with either "-pedantic-errors -std=c17" or
    "-pedantic-errors -std=c23".)

    I'm actually not sure what specific changes were made in C23 in
    this area. I've compared the "Simple assignment" sections of C17
    and C23 (drafts), and the only significant changes were to allow for
    values of type nullptr_t.
    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */
    --- Synchronet 3.21f-Linux NewsLink 1.2
  • From Michael S@already5chosen@yahoo.com to comp.lang.c on Thu Apr 9 03:09:37 2026
    From Newsgroup: comp.lang.c

    On Wed, 08 Apr 2026 15:57:05 -0700
    Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:

    Michael S <already5chosen@yahoo.com> writes:
    IIRC, this is my first on-topic post in comp.lang.c group.
    As they say, nobody is perfect.

    // pedant_2d.c
    int bar(const int a[5][42])
    {
    return a[0][0];
    }

    int foo(int x)
    {
    int a[5][42];
    a[0][0] = 1;
    return bar(a);
    }

    // end of pedant_2d.c


    $ gcc -std=c17 -pedantic -c pedant_2d.c
    pedant_2d.c: In function 'foo':
    pedant_2d.c:10:14: warning: invalid use of pointers to arrays with different qualifiers in ISO C before C23 [-Wpedantic] 10 | return
    bar(a); | ^

    What does it mean ?

    This is an incomplete answer. Perhaps someone else can fill in
    some more details.

    Given the parameter declaration `const int a[5][42]`, a is of
    course a pointer, not an array. Specifically, it's of type `constint(*)[42])`, or "pointer to array 42 of const int".
    (The 5 is quietly ignored.)

    The argument `a` in the call (it might have been clearer to use
    distinct names) is of type `int[5][42]`, or "array 5 of array
    42 of int". In the call, as in most contexts, it decays to
    `int (*)[42]`, or "pointer to array 42 of int".

    The problem is that the types "pointer to array N of int" and
    "pointer to array N of const int" are distinct and are not assignment-compatible.



    int bar(const int a[42])
    {
    return a[0];
    }

    int foo(int x)
    {
    int a[42];
    a[0] = 1;
    return bar(a);
    }

    That, of course, compile with no wraning.
    So, 'pointer to int' is, according to gcc, assignment-compatible (on
    the right side) with 'pointer to const int', but in case of pointers to
    arrays it is not the same?
    IMHO, if the standard really says that then every reasonable compiler
    shall ignore the Standard and follow practicality.

    This seems to be a case where forbidding assignment does not promote
    safety. Assigning a `const int*` value to an `int*` object, if
    it were allowed, would permit bypassing const correctness, such as
    quietly permitting an attempt to assign a value to a const object.
    But prior to C23, the rules were a bit more strict than they need
    to be.

    A simple example:

    int main(void) {
    int (*x)[10];
    const int (*y)[10];
    x = y; // discards "const"
    y = x;
    }

    The first assignment is a constraint violation in all versions of
    standard C because it discards the "const" qualifier. The second
    gets the "before C23" diagnostic with gcc. (clang doesn't
    complain about it with either "-pedantic-errors -std=c17" or "-pedantic-errors -std=c23".)

    I'm actually not sure what specific changes were made in C23 in
    this area. I've compared the "Simple assignment" sections of C17
    and C23 (drafts), and the only significant changes were to allow for
    values of type nullptr_t.



    --- Synchronet 3.21f-Linux NewsLink 1.2
  • From jmj@jmj@energokod.gda.pl to comp.lang.c on Thu Apr 9 02:33:37 2026
    From Newsgroup: comp.lang.c

    W dniu 9.04.2026 o 02:09, Michael S pisze:
    IMHO, if the standard really says that then every reasonable compiler
    shall ignore the Standard and follow practicality.

    But if they drop the Standard and made each C lang compiler in different
    way then: How to write portable code in C lang?
    --
    Jacek Marcin Jaworski, Pruszcz Gd., woj. Pomorskie, Polska 🇵🇱, EU 🇪🇺;
    tel.: +48-609-170-742, najlepiej w godz.: 5:00-5:55 lub 16:00-17:25; <jmj@energokod.gda.pl>, gpg: 4A541AA7A6E872318B85D7F6A651CC39244B0BFA;
    Domowa s. WWW: <https://energokod.gda.pl>;
    Mini Netykieta: <https://energokod.gda.pl/MiniNetykieta.html>;
    Mailowa Samoobrona: <https://emailselfdefense.fsf.org/pl>.
    UWAGA:
    NIE ZACIĄGAJ "UKRYTEGO DŁUGU"! PŁAĆ ZA PROG. FOSS I INFO. INTERNETOWE! CZYTAJ DARMOWY: "17. Raport Totaliztyczny - Patroni Kontra Bankierzy": <https://energokod.gda.pl/raporty-totaliztyczne/17.%20Patroni%20Kontra%20Bankierzy.pdf>

    --- Synchronet 3.21f-Linux NewsLink 1.2
  • From Andrey Tarasevich@noone@noone.net to comp.lang.c on Wed Apr 8 20:34:43 2026
    From Newsgroup: comp.lang.c

    On Wed 4/8/2026 3:21 PM, Michael S wrote:

    What does it mean ?


    I already mentioned this issue here not so long ago.

    Again, an old puzzle about C const-correctness rules goes as follows:

    Is the following initialization

    T t;
    const T *pt = &t;

    valid for all complete object types `T`?

    And the answer is "no". The above initialization is not valid if `T` is
    an array type. E.g. given

    typedef int T[10];

    the above initialization contains a constraint violation. Your original example with 2D array is actually exactly the same thing, once you take
    into account function argument type adjustment.

    This strange behavior is prescribed by

    6.7.3 Type qualifiers
    9 If the specification of an array type includes any type qualifiers,
    the element type is so qualified, not the array type.

    (quoted from C11)

    So, the fact that the `const` qualifier applied to array type "falls
    through" to individual array elements is what prevents this
    initialization from working. There's simply no provision in other rules
    of the language (re: assignment constraints) that would permit
    initialization of "pointer to an array of non-const elements" with a
    "pointer to an array of const elements".

    C23 changed the above to the following:

    6.7.4 Type qualifiers
    6.7.4.1 General
    10 If the specification of an array type includes any type
    qualifiers, both the array and the element type are so-qualified.

    I.e. now const-qualification applies not only to the elements, but also
    to the entire array type as well. With this change in place the above initialization becomes valid. It now falls under the "usual"
    const-correctness rules. In C23 arrays are no longer special in such
    contexts.
    --
    Best regards,
    Andrey

    --- Synchronet 3.21f-Linux NewsLink 1.2
  • From Tim Rentsch@tr.17687@z991.linuxsc.com to comp.lang.c on Wed Apr 8 21:42:22 2026
    From Newsgroup: comp.lang.c

    Michael S <already5chosen@yahoo.com> writes:

    On Wed, 08 Apr 2026 15:57:05 -0700
    Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:

    Michael S <already5chosen@yahoo.com> writes:

    IIRC, this is my first on-topic post in comp.lang.c group.
    As they say, nobody is perfect.

    // pedant_2d.c
    int bar(const int a[5][42])
    {
    return a[0][0];
    }

    int foo(int x)
    {
    int a[5][42];
    a[0][0] = 1;
    return bar(a);
    }

    // end of pedant_2d.c


    $ gcc -std=c17 -pedantic -c pedant_2d.c
    pedant_2d.c: In function 'foo':
    pedant_2d.c:10:14: warning: invalid use of pointers to arrays with
    different qualifiers in ISO C before C23 [-Wpedantic] 10 | return
    bar(a); | ^

    What does it mean ?

    This is an incomplete answer. Perhaps someone else can fill in
    some more details.

    Given the parameter declaration `const int a[5][42]`, a is of
    course a pointer, not an array. Specifically, it's of type
    `constint(*)[42])`, or "pointer to array 42 of const int".
    (The 5 is quietly ignored.)

    The argument `a` in the call (it might have been clearer to use
    distinct names) is of type `int[5][42]`, or "array 5 of array
    42 of int". In the call, as in most contexts, it decays to
    `int (*)[42]`, or "pointer to array 42 of int".

    The problem is that the types "pointer to array N of int" and
    "pointer to array N of const int" are distinct and are not
    assignment-compatible.

    int bar(const int a[42])
    {
    return a[0];
    }

    int foo(int x)
    {
    int a[42];
    a[0] = 1;
    return bar(a);
    }

    That, of course, compile with no wraning.
    So, 'pointer to int' is, according to gcc, assignment-compatible (on
    the right side) with 'pointer to const int', but in case of pointers to arrays it is not the same?

    The analogous case with pointers is not const int * but const int **,
    which also fails if given an int **. Andrey Tarasevich gave an
    explanation.

    IMHO, if the standard really says that then every reasonable compiler
    shall ignore the Standard and follow practicality.

    I think that's an overreaction. This particular problem doesn't come
    up very often. When it does, usually it's easy to fix just by writing
    a wrapper function and calling that instead:

    int bar(const int a[5][42])
    {
    return a[0][0];
    }

    int bas(int a[5][42]){
    return bar( (const int (*)[42]) a );
    }

    int foo(int x)
    {
    int a[5][42];
    a[0][0] = 1;
    return bas(a);
    }
    --- Synchronet 3.21f-Linux NewsLink 1.2
  • From Michael S@already5chosen@yahoo.com to comp.lang.c on Thu Apr 9 11:09:24 2026
    From Newsgroup: comp.lang.c

    On Wed, 8 Apr 2026 20:34:43 -0700
    Andrey Tarasevich <noone@noone.net> wrote:

    On Wed 4/8/2026 3:21 PM, Michael S wrote:

    What does it mean ?


    I already mentioned this issue here not so long ago.

    Again, an old puzzle about C const-correctness rules goes as follows:

    Is the following initialization

    T t;
    const T *pt = &t;

    valid for all complete object types `T`?

    And the answer is "no". The above initialization is not valid if `T`
    is an array type. E.g. given

    typedef int T[10];

    the above initialization contains a constraint violation. Your
    original example with 2D array is actually exactly the same thing,
    once you take into account function argument type adjustment.

    This strange behavior is prescribed by

    6.7.3 Type qualifiers
    9 If the specification of an array type includes any type
    qualifiers, the element type is so qualified, not the array type.

    (quoted from C11)

    So, the fact that the `const` qualifier applied to array type "falls through" to individual array elements is what prevents this
    initialization from working. There's simply no provision in other
    rules of the language (re: assignment constraints) that would permit initialization of "pointer to an array of non-const elements" with a "pointer to an array of const elements".

    C23 changed the above to the following:

    6.7.4 Type qualifiers
    6.7.4.1 General
    10 If the specification of an array type includes any type
    qualifiers, both the array and the element type are so-qualified.

    I.e. now const-qualification applies not only to the elements, but
    also to the entire array type as well. With this change in place the
    above initialization becomes valid. It now falls under the "usual" const-correctness rules. In C23 arrays are no longer special in such contexts.


    Thank you.

    --- Synchronet 3.21f-Linux NewsLink 1.2
  • From Chris M. Thomasson@chris.m.thomasson.1@gmail.com to comp.lang.c on Thu Apr 9 02:38:56 2026
    From Newsgroup: comp.lang.c

    On 4/8/2026 8:34 PM, Andrey Tarasevich wrote:
    On Wed 4/8/2026 3:21 PM, Michael S wrote:

    What does it mean ?


    I already mentioned this issue here not so long ago.

    Again, an old puzzle about C const-correctness rules goes as follows:

      Is the following initialization

        T t;
        const T *pt = &t;

      valid for all complete object types `T`?

    And the answer is "no". The above initialization is not valid if `T` is
    an array type. E.g. given

      typedef int T[10];

    the above initialization contains a constraint violation. Your original example with 2D array is actually exactly the same thing, once you take
    into account function argument type adjustment.

    This strange behavior is prescribed by

      6.7.3 Type qualifiers
      9 If the specification of an array type includes any type qualifiers, the element type is so qualified, not the array type.

    (quoted from C11)

    So, the fact that the `const` qualifier applied to array type "falls through" to individual array elements is what prevents this
    initialization from working. There's simply no provision in other rules
    of the language (re: assignment constraints) that would permit initialization of "pointer to an array of non-const elements" with a "pointer to an array of const elements".

    C23 changed the above to the following:

      6.7.4 Type qualifiers
      6.7.4.1 General
      10 If the specification of an array type includes any type
    qualifiers, both the array and the element type are so-qualified.

    I.e. now const-qualification applies not only to the elements, but also
    to the entire array type as well. With this change in place the above initialization becomes valid. It now falls under the "usual" const- correctness rules. In C23 arrays are no longer special in such contexts.


    self pointers for an api type const* const ptr? ;^)
    --- Synchronet 3.21f-Linux NewsLink 1.2
  • From Michael S@already5chosen@yahoo.com to comp.lang.c on Thu Apr 9 14:06:02 2026
    From Newsgroup: comp.lang.c

    On Thu, 9 Apr 2026 02:38:56 -0700
    "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com> wrote:
    On 4/8/2026 8:34 PM, Andrey Tarasevich wrote:
    On Wed 4/8/2026 3:21 PM, Michael S wrote:

    What does it mean ?


    I already mentioned this issue here not so long ago.

    Again, an old puzzle about C const-correctness rules goes as
    follows:

    Is the following initialization

    T t;
    const T *pt = &t;

    valid for all complete object types `T`?

    And the answer is "no". The above initialization is not valid if
    `T` is an array type. E.g. given

    typedef int T[10];

    the above initialization contains a constraint violation. Your
    original example with 2D array is actually exactly the same thing,
    once you take into account function argument type adjustment.

    This strange behavior is prescribed by

    6.7.3 Type qualifiers
    9 If the specification of an array type includes any type
    qualifiers, the element type is so qualified, not the array type.

    (quoted from C11)

    So, the fact that the `const` qualifier applied to array type
    "falls through" to individual array elements is what prevents this initialization from working. There's simply no provision in other
    rules of the language (re: assignment constraints) that would
    permit initialization of "pointer to an array of non-const
    elements" with a "pointer to an array of const elements".

    C23 changed the above to the following:

    6.7.4 Type qualifiers
    6.7.4.1 General
    10 If the specification of an array type includes any type
    qualifiers, both the array and the element type are so-qualified.

    I.e. now const-qualification applies not only to the elements, but
    also to the entire array type as well. With this change in place
    the above initialization becomes valid. It now falls under the
    "usual" const- correctness rules. In C23 arrays are no longer
    special in such contexts.

    self pointers for an api type const* const ptr? ;^)
    Are you able to decipher your own posts 1 hour later?
    --- Synchronet 3.21f-Linux NewsLink 1.2
  • From Chris M. Thomasson@chris.m.thomasson.1@gmail.com to comp.lang.c on Thu Apr 9 13:09:51 2026
    From Newsgroup: comp.lang.c

    On 4/9/2026 4:06 AM, Michael S wrote:
    On Thu, 9 Apr 2026 02:38:56 -0700
    "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com> wrote:

    On 4/8/2026 8:34 PM, Andrey Tarasevich wrote:
    On Wed 4/8/2026 3:21 PM, Michael S wrote:

    What does it mean ?


    I already mentioned this issue here not so long ago.

    Again, an old puzzle about C const-correctness rules goes as
    follows:

      Is the following initialization

        T t;
        const T *pt = &t;

      valid for all complete object types `T`?

    And the answer is "no". The above initialization is not valid if
    `T` is an array type. E.g. given

      typedef int T[10];

    the above initialization contains a constraint violation. Your
    original example with 2D array is actually exactly the same thing,
    once you take into account function argument type adjustment.

    This strange behavior is prescribed by

      6.7.3 Type qualifiers
      9 If the specification of an array type includes any type
    qualifiers, the element type is so qualified, not the array type.

    (quoted from C11)

    So, the fact that the `const` qualifier applied to array type
    "falls through" to individual array elements is what prevents this
    initialization from working. There's simply no provision in other
    rules of the language (re: assignment constraints) that would
    permit initialization of "pointer to an array of non-const
    elements" with a "pointer to an array of const elements".

    C23 changed the above to the following:

      6.7.4 Type qualifiers
      6.7.4.1 General
      10 If the specification of an array type includes any type
    qualifiers, both the array and the element type are so-qualified.

    I.e. now const-qualification applies not only to the elements, but
    also to the entire array type as well. With this change in place
    the above initialization becomes valid. It now falls under the
    "usual" const- correctness rules. In C23 arrays are no longer
    special in such contexts.

    self pointers for an api type const* const ptr? ;^)


    Are you able to decipher your own posts 1 hour later?




    ;^)

    Yes, say a const pointer to a const object?


    struct foo const* const ?

    Here is some old C code of mine for a test:


    /* Interfaces ____________________________________________________________________*/
    #include <stddef.h>


    struct object_prv_vtable {
    int (*fp_destroy) (void* const);
    };


    struct device_prv_vtable {
    int (*fp_read) (void* const, void*, size_t);
    int (*fp_write) (void* const, void const*, size_t);
    };


    struct device_vtable {
    struct object_prv_vtable const object;
    struct device_prv_vtable const device;
    };


    struct device {
    struct device_vtable const* vtable;
    };


    #define object_destroy(mp_self) ( \
    (mp_self)->vtable->object.fp_destroy((mp_self)) \
    )


    #define device_read(mp_self, mp_buf, mp_size) ( \
    (mp_self)->vtable->device.fp_read((mp_self), (mp_buf), (mp_size)) \
    )


    #define device_write(mp_self, mp_buf, mp_size) ( \
    (mp_self)->vtable->device.fp_write((mp_self), (mp_buf), (mp_size)) \
    )






    /* Sample Header (usb_drive.h) ____________________________________________________________________*/
    #if ! defined(USB_HEADER_H)
    #define USB_HEADER_H


    extern int usb_drive_create(struct device** const);


    #endif







    /* Sample Impl (usb_drive.c) ____________________________________________________________________*/
    /* #include "usb_drive.c" */
    #include <stdio.h>
    #include <stdlib.h>


    struct usb_drive {
    struct device device;
    /* whatever */
    };


    static int usb_drive_object_destroy(void* const);
    static int usb_drive_device_read(void* const, void*, size_t);
    static int usb_drive_device_write(void* const, void const*, size_t);


    static struct device_vtable const g_table = {
    { /* object */
    usb_drive_object_destroy
    },

    { /* device */
    usb_drive_device_read,
    usb_drive_device_write
    }
    };


    int usb_drive_create(
    struct device** const pself
    ) {
    struct usb_drive* const self = malloc(sizeof(*self));
    if (self) {
    self->device.vtable = &g_table;
    *pself = &self->device;
    return 0;
    }
    return -1;
    }


    int usb_drive_object_destroy(
    void* const self_
    ) {
    struct usb_drive* const self = self_;
    printf("usb_drive_object_destroy(%p)\n", (void*)self);
    free(self_);
    return 0;
    }


    int usb_drive_device_read(
    void* const self_,
    void* buf,
    size_t size
    ) {
    struct usb_drive* const self = self_;
    printf("usb_drive_device_read(%p, %p, %lu)\n",
    (void*)self, buf, (unsigned long)size);
    return 0;
    }


    int usb_drive_device_write(
    void* const self_,
    void const* buf,
    size_t size
    ) {
    struct usb_drive* const self = self_;
    printf("usb_drive_device_write(%p, %p, %lu)\n",
    (void*)self, buf, (unsigned long)size);
    return 0;
    }







    /* Sample Application ____________________________________________________________________*/
    void read_write(
    struct device* const self
    ) {
    char buf[100];

    device_read(self, buf, 50);

    device_write(self, buf, 5);
    }


    int main(void) {
    struct device* a_device;

    if (! usb_drive_create(&a_device)) {
    read_write(a_device);

    object_destroy(a_device);
    }

    return 0;
    }



    --- Synchronet 3.21f-Linux NewsLink 1.2
  • From Andrey Tarasevich@noone@noone.net to comp.lang.c on Sun Apr 12 11:30:18 2026
    From Newsgroup: comp.lang.c

    On Thu 4/9/2026 1:09 PM, Chris M. Thomasson wrote:

    struct foo const* const ?

    Again, the ability to implicitly convert `T **` to `const T* const*`
    seems to be related to the above array pointer conversion issue (at
    least superficially). In C++ both conversions are supported as standard
    (i.e. implicit) conversions. In "classic" C neither is, which is a bit annoying.

    C23 resolved the array issue. But the `T ** -> const T* const*`
    conversion is still unsupported as an implicit conversion even in C23.
    --
    Best regards,
    Andrey



    --- Synchronet 3.21f-Linux NewsLink 1.2
  • From Tim Rentsch@tr.17687@z991.linuxsc.com to comp.lang.c on Mon Apr 13 05:40:50 2026
    From Newsgroup: comp.lang.c

    Andrey Tarasevich <noone@noone.net> writes:

    On Thu 4/9/2026 1:09 PM, Chris M. Thomasson wrote:

    struct foo const* const ?

    Again, the ability to implicitly convert `T **` to `const T* const*`
    seems to be related to the above array pointer conversion issue (at
    least superficially). In C++ both conversions are supported as
    standard (i.e. implicit) conversions. In "classic" C neither is,
    which is a bit annoying.

    C23 resolved the array issue. But the `T ** -> const T* const*`
    conversion is still unsupported as an implicit conversion even in C23.

    The circumstances with arrays and with pointers are rather
    different. With pointers, one can always convert to a more
    const-laden version of a type by using a cast:

    int
    takes_ppi( int **p ){
    return **p;
    }

    int
    takes_ppci( const int **p ){
    return **p;
    }

    int
    takes_pcpi( int *const *p ){
    return **p;
    }

    int
    takes_pcpci( const int *const *p ){
    return **p;
    }

    int
    try_each_pointer_type( int **p ){
    int a = takes_ppi( p );
    int b = takes_ppci( (const int **) p );
    int c = takes_pcpi( p );
    int d = takes_pcpci( (const int *const*) p );
    return a+b+c+d;
    }

    Notice in the case of takes_pcpi() that no cast is needed. That's
    because the rule that 'X*' may be converted to 'const X*' applies
    even when 'X' is a pointer type. Incidentally, the 'takes_ppci()'
    case gives a warning under gcc with -Wall -Wextra, so using a cast
    is "safe" in the sense that it is possible to get a warning for a
    dangerous conversion.

    The problem with arrays is that there is no way to convert from an
    argument of type 'X*' to type 'const X*' when X is an array type,
    because before C23 "const array" types _didn't exist_. Thus there
    is no way to satisfy the rules for pointer conversion just by
    writing a cast. There is a way around the problem of wanting to
    convert to, for example, a 'const int (*)[1]' type, but it's more
    cumbersome:

    int
    takes_pai( int (*p)[1] ){
    return (*p)[0];
    }

    int
    takes_paci( const int (*p)[1] ){
    return (*p)[0];
    }

    int
    try_each_array_type( int (*p)[1] ){
    union { int (*pai)[1]; const int (*paci)[1]; } both = {p};
    int a = takes_pai( p );
    int b = takes_paci( both.paci );
    return a+b;
    }

    Mixing const-ness and pointer-ness is tricky, or at least
    non-obvious. I'm not sure the C decision regarding implicit
    conversions when const-ness and pointer-ness are both involved is a
    bad choice. Since it doesn't come up very often, and the remedy for
    when it does come up is so straightforward, it might be better to
    adopt a simple rule, as C has done, than to complicate the language
    definition with a more elaborate rule. But arrays are another
    kettle of fish altogether, and I'm happy to see the issues around
    const arrays are finally getting the attention they deserve (which I
    have been advocating for more than 10 years now).
    --- Synchronet 3.21f-Linux NewsLink 1.2