Consider this code:
void test(int** p)
{
}
int main()
{
int arr[] = {30, 450, 14, 5};
test(&arr);
return 0;
}
gcc
isn't very happy about it, and issues a warning: passing arg 1
of test from incompatible pointer type
. C++ has stricter type
checking, so let's try running the same code through g++
. As
expected, we get an error: cannot convert int (*)[4] to int** for
argument 1 to void test(int**)
.
So what's the problem here? What's wrong with the code above? Well,
everything. It's simply invalid, and it makes no sense. Some would
think it should work because this works:
void test(int* p)
{
}
int main()
{
int arr[] = {30, 450, 14, 5};
test(arr);
return 0;
}
But this one works specifically because the C compilers should follow
the C standard, which mandates that arrays "decay" into pointers when
used as lvalues. Thus, a pointer to the array's first element is
actually passed to test and everything works.
However, the first code snippet is different. While an array name may
decay into a pointer, the address of the array does not decay into a
pointer to a pointer. And why should it? What sense does it make to
treat an array so?
Pointers to pointers are sometimes passed to modify the pointers
(simple pointer arguments don't work here because C passes by value,
which would only allow to modify what's pointed, not the pointer
itself). Here's some imaginary code (won't compile):
void test(int** p)
{
*p = malloc ... /* retarget '*p' */
}
int main()
{
int arr[] = {30, 450, 14, 5};
int* ptr;
/* Fine!
** test will retarget ptr, and its new value
** will appear after this call.
*/
test(&ptr);
/* Makes no sense!
** You cannot retarget 'arr', since it's a
** constant label created by the compiler.
*/
test(&arr);
return 0;
}
Pointers to arrays
Note that the original code could be modified a little to make it
work:
void test(int (*p)[4])
{
(*p)[2] = 10;
}
int main()
{
int arr[] = {30, 450, 14, 5};
test(&arr);
printf("%d\n", arr[2]);
return 0;
}
What is that weird type test accepts now? Say hello to a "pointer to
array", one of the useless features of C. This is what the C FAQ has
to say about it:
2.12: How do I declare a pointer to an array?
Usually, you don't want to. When people speak casually of a pointer to
an array, they usually mean a pointer to its first element.
While the test function from the previous snippet compiles and works,
it isn't of much use, since it's much clearer to write:
void test(int* p)
{
p[2] = 10;
}
...
...
/* then call */
test(arr);
The main use of pointers as function arguments is to either avoid
passing whole structures by value, or to modify the object pointed by
the pointers. Both are irrelevant needs for pointers to array. Here's
a clarifying snippet:
int joe[] = {1, 2, 3, 4};
void test(int (*p)[4])
{
/* Fine: assign to an element through the
** pointer.
*/
(*p)[2] = 10;
/* Works, but won't be reflected in the
** caller since p was passed by value.
*/
p = &joe;
/* Error: arrays can't be assigned.
*/
*p = joe;
}
Arrays are not passed by value anyway, so a pointer to an array is
useless for this purpose. Neither can arrays be modified, so that
kills the second reason.