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.
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.