Search code examples
cfunction-declaration

Difference between const pointer and const array type in function parameter declaration


If I try to compile this small example:

void f(const int *a) {
    int * ptr = a;
    *ptr = 1;
}

void g(const int a[]) {
    int * ptr = a;
    *ptr = 1;
}

I get a warning for function f and an error for function g. I tried with gcc 9.3.0 and clang 10.0.0 and got very similar messages. Here the one from gcc:

main.c: In function ‘f’:
main.c:4:16: warning: initialization discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
    4 |     int* ptr = a;
      |                ^
main.c: In function ‘g’:
main.c:10:5: error: invalid operands to binary * (have ‘const int *’ and ‘int *’)
   10 |     *ptr = 1;
      |     ^

I know there is a difference between arrays and pointers, but I always believed there is no difference in function parameter declaration. And isn't using an array and a pointer as left hand operand in an assignment completely equivalent? After all I also thought that the following were interchangeable:

int main(int argc, char * argv[])

int main(int argc, char ** argv)

My questions:

  • What is the exact difference between const int * a and const int a[] in a function parameter declaration?

  • Why do I get a warning in one of the cases and an error in the other?

  • Shouldn't be both cases errors, because I try to change a const parameter?

Edit as Paul Hankin pointed out I missed a semicolon in the original code. If added, both functions indeed give the same warning message, which is OK, since implicit pointer conversions (like in ptr = a discard const qualifiers.


Solution

  • I get exactly the same messages for both functions in both gcc and clang. You have some typo in the code and didn't make a copy/paste when you posted it here.

    I always believed there is no difference in function parameter declaration.

    This is true for plain 1D arrays. An array parameter such as const int a[] will get adjusted ("decay") into a pointer to it's first element. It is 100% equivalent to const int* a. Note that the const qualifier belongs to the pointed-at type, not the pointer itself.

    Now as it happens, incomplete types such as arrays without a size cannot actually be used as function parameters if not for this array adjustment rule1). It's the very reason why you can even type out an incomplete array type [] without size as a function parameter, because the type gets adjusted before the size of the array matters and the result is not an incomplete type but a pointer (to a complete type).

    This equivalence doesn't scale well to 2D arrays however. An array parameter such as const int a[x][y] is an array of arrays. The first item is of array type const int[y]. So it gets adjusted to a pointer to such a type:
    const (*int)[y]. This is not compatible with const int* nor with const int**.


    1) C17 6.7.6.3

    After adjustment, the parameters in a parameter type list in a function declarator that is part of a definition of that function shall not have incomplete type.