[ Team LiB ] |
![]() ![]() |
Gotcha #34: Pointer-to-Multidimensional-Array ProblemsC and C++ arrays are pretty minimal. In fact, an array name is really not much more than a pointer literal that refers to the first element of the array: int a[5]; int * const pa = a; int * const *ppa = &pa; const int alen = sizeof(a)/sizeof(a[0]); // alen == 5 The only practical differences between an array name and a constant pointer are that the array name gives the array size in a sizeof expression rather than giving the size of a pointer, and an array name occupies no storage and therefore has no address. To be clear: an array has an address, and that address is indicated by the array name; the array name itself has no address: int *ip = a; // a is a ptr to first element of array int (*ap)[5] = &a; // &a is the address of the array, not a int (*ap2)[sizeof(a)/sizeof(a[0])] = &a; // same thing int **pip = &ip; // &ip is the address of a pointer, not an array This is also the case for multidimensional arrays or, more properly, arrays of arrays. But remember that the type of the first element of a multidimensional array is an array, not the base type: int aa[2][3]; const int aalen = sizeof(aa)/sizeof(aa[0]); // aalen = 2 Therefore, aa is essentially a pointer literal to the first element of an array of three integers. It's not a pointer to an integer. This can lead to some surprising, if technically correct, results:
void processElems( int *, size_t );
void processElems( void *, size_t );
// . . .
processElems( a, alen );
processElems( aa, aalen ); // oops!
The first call to the overloaded processElems function matches the version that takes an int * argument; the array name a is just an int * in disguise. The second call matches the version of processElem that takes a void *, which is probably not what the programmer intended. The type of the multidimensional array name is a pointer to its first element, which is an array of a particular size, not a pointer to the base type of the array. There is no implicit conversion of an int(*)[3] (that is, a pointer to an array of three integers) to an int *, but there is such a conversion to a void *. int (* const paa)[3] = aa; int (* const *ppaa)[3] = &paa; void processElems( int (*)[3], size_t ); // . . . processElems( aa, aalen ); // OK. Multidimensional arrays are problematic. A better alternative is generally to use the standard library containers or special-purpose containers that implement abstract multidimensional arrays. If a situation does require the use of raw multidimensional arrays, encapsulating them is usually best. It's just not responsible to expose a naive user of your interface to
int *(*(*aryCallback)(int *(*)[n]))[n];
This is (of course) a pointer to a function that takes a pointer to an array of n pointers to int and returns a pointer of the same type. All right, that's just showing off. (See Gotcha #11.) A typedef would have simplified things considerably: typedef int *(*PA)[n]; PA (*aryCallback)(PA); // more humane |
[ Team LiB ] |
![]() ![]() |