I am confused with pointers and arrays to be honest. It took me a long time to figure out how to pass a 2D array.
This confusion is very widespread. It stems from the fact that you cannot pass arrays in C, but the language allows a syntax that makes it look like you could.
So, as you can't pass an array to a function, what you do instead is pass it a pointer to the first array element and, if needed, the size of the array. Inside the function, you can use that pointer for accessing the array, and the syntax looks the same as if it would be an actual array. This also needs some explanation:
[]
, the indexing operator, works by adding the index to a given pointer and dereferencing the resulting pointer. Therefore, writing a[5]
is exactly the same as *(a+5)
.
- This even works if
a
is an actual array (and not a pointer), because an array evaluates to a pointer to the first element in most contexts (there are exceptions like the sizeof
operator).
To make things more complicated, C allows to declare functions that look as if they were taking arrays, e.g. you can write:
void foo(int bar[]);
There's a rule in the C standard that says function parameter types are subject to "type adjustment": Any parameter of an array type is automatically adjusted to the corresponding pointer type. So, the function above is actually the same as
void foo(int *bar);
Therefore, better forget about passing arrays (I'd even recommend to not use the array syntax in function parameters, but that's subject to debate) -- you always pass pointers.
With this knowledge, you can easily construct correct examples for all your cases:
(1) "Normal" array:
void foo(int bar[], size_t n); // with type adjustment
void foo(int *bar, size_t n); // what it really means
// call like
int a[5];
foo(a, 5);
(2) 2D array:
A 2D array is an array of arrays, so it's first element is itself an array -> you would pass a pointer to an array
void foo(int bar[][10], int n); // with type adjustment, 2d array of n x 10
void foo(int (*bar)[10], int n); // what it really means
// call like:
int a[5][10];
foo(a, 5);
(3) Array of pointers:
An array of pointers is often used as an alternative to a 2d array -- as the elements are just pointers, they can point to single values or arrays of different lengths, but the drawback is that you don't have the whole data as a single block in memory, as would be the case with a real 2d array. As this is just an array of pointers, it looks very much like a "normal" array in use:
void foo(int *bar[], int n); // with type adjustment
void foo(int **bar, int n); // what it really means
// call like:
int *a[5];
foo(a, 5);
Final note: You will often read that arrays "decay as pointers" in C. That's no official wording, but very widespread. If you combine the rule that an array identifier evaluates to a pointer in most contexts (e.g. when passing it to a function) with the rule of type adjustment for function parameters, the result is that you write array syntax everywhere, but get pointers, so that's the meaning of "decaying".