When passing parameters to a function they are copied "as if by assignment", meaning that the parameter copy follows the same rules as the =
operator, formally called simple assignment. So what your code is actually doing between the lines, is essentially the same as this:
int** arr1 = ... ;
const int** arr2 = arr1;
And if you try to compile that snippet, you'll get almost the same error message, saying something like "initialization from incompatible type".
When doing simple assignment, the rule for copying pointers is (simplified):
- The left operand (of the
=
operator) can have either qualified or unqualified pointer type. Qualified meaning for example const
.
- Both operands must be pointers to qualified or unqualified versions of a compatible type.
- The type pointed to by the left must have at least all the qualifiers of the type pointed to by the right. (Meaning
int x; const int y = x;
is ok but not the other way around.)
For the case where you have int* x; const int* y = x;
the compiler won't complain. y
is a qualified pointer to int type, x
is an unqualified pointer to int type. y
has at least all the qualifiers of x
. All of the above mentioned rules are fulfilled, so this is fine.
The problem here is how qualifiers behave together with pointer-to-pointer. const int**
actually means (read it from right to left) "pointer to pointer to const int". It does not mean "const pointer to pointer to int".
If we go back to the first example of const int** arr2 = arr1;
, arr2 is a unqualified pointer to type const int*
. And arr1 is an unqualified pointer to type int*
. They are not compatible types - it doesn't matter that arr2
happens to point at a type which is a qualified version of what arr1
points at. The rules only care about the "outermost" pointer types themselves.
To fix this and maintain "const correctness", we would have to const qualify the pointer-to-pointer itself. That would be int**const arr
. Again, read right to left: "const pointer to pointer to int".
My recommendation would however be to get rid of the pointer to pointer entirely, since your code is needlessly slow because of it. The multiple malloc calls will lead to fragmented allocation and poor data cache utilization. Instead you can use pointer to VLA like this:
#include <stdlib.h>
void iter_2d (size_t row,
size_t column,
int arr[row][column]);
int main (void){
const size_t row = 4;
const size_t column = 3;
int (*arr)[column] = malloc( sizeof(int[row][column]) );
iter_2d(row, column, arr);
free(arr);
}
This code is much faster, less complex, easier to read.
More info: Correctly allocating multi-dimensional arrays.