简体   繁体   中英

C++ multidimensional array and pointers to table of pointers

Here is the thing. I can completely understand a concept of a multidimensional array (let's consider 2D for a while) made by pointer to array to pointers and so on...

We do something like this:

// we can use dynamically defined size n and m
int n = 3, m = 5 ;
int **T = new int *[n];
for (int i = 0 ; i < n ; ++i)
    T[i] = new int[m];

What we got is: (Check if I'm right here)

  • 3 blocks of memory of 5 ints, placed somewhere in memory
  • One additional block of memory of same size as number of the blocks of ints (number of rows). This block is an array of pointers (usually 4 bytes for pointer like int) to those int rows.
  • What we are interested in most - that is T which is of type (**T) - a pointer to pointer. Which is exactly a pointer to an array of pointer, because in C++ an array is in fact a pointer pointing to a block of memory so t[] or t[0] means *t, and t[x] means *(t+x).

Now the problem is when we do sth like this:

int n = 3, m = 5 ;
int T[n][m] ;

What we've got is not what w could have doing the thing I showed before. We get sth strange. What is T? When printfing T, we get the same value as T[0]. It looks like we reserved a block of ints sized n*m without additional array of pointers to rows.

My question is: does the compiler remembers the dimension of the array and number of rows and columns? And when asking for T[i][j] it actually asks for *(T+i*n+j) so this n is stored somewhere? The problem is when we are trying to pass this thing (T) to a function. I dont know why but if n and m were constants its possible to pass T as a pointer to this array to function like in this program:

#include <stdio.h>
const int n = 3, m = 4 ; // this is constant!
void add_5(int K[][m])
{
    for (int i = 0 ; i < n ; ++i)
        for (int j = 0 ; j < m ; j++)
            K[i][j] += 5 ;
}
int main()
{
    // n x m array the most straight forward method
    int T[n][m] ;
    for (int i = 0 ; i < n ; ++i)
        for (int j = 0 ; j < m ; ++j)
            T[i][j] = i*m + j ;

    for (int i = 0 ; i < n ; ++i)
    {
        for (int j = 0 ; j < m ; j++)
            printf("%d ",T[i][j]) ;
        printf("\n") ;
    }
    printf("\n") ;

    // adding 5 to all cells
    add_5(T) ;
    printf("it worked!!\n") ;

    for (int i = 0 ; i < n ; ++i)
    {
        for (int j = 0 ; j < m ; j++)
            printf("%d ",T[i][j]) ;
        printf("\n") ;
    }

    int ss ;
    scanf("%d",&ss) ;
}

But if n and m aren't constant we cant. So what I need is to pass dynamically created multidimensional array's pointer to a function without manually allocating memory for that. How to do this?

in C++ an array is in fact a pointer pointing to a block of memory

Absolutely not. An array is entirely separate from a pointer. The reason you might have this confusion is because of a standard conversion called array-to-pointer conversion. Consider:

int arr[10];

The variable arr denotes an array. It's not a pointer at all. It just so happens that in many circumstances, the name of an array will be converted to a pointer to its first element. That is, the conversion turns it into an int* .

int T[n][m] ;

In this case, T is an "array of n array of m int s". You mentioned that printing both T and T[0] give the same result, and this is due to array-to-pointer conversion.

  1. The expression T can be converted to a pointer to the first element; that is, an int (*)[m] , because the first element of T is itself an array with m elements.

  2. The expression T[0] can be converted to a pointer to the first element of the first subarray. So you get a pointer to the element T[0][0] of type int* .

These pointers hold the same address because of the way an array is laid out in memory. The address that an array begins at is the same address as the first element of that array. However, the pointers do not behave in the same way. If you increment the pointer resulting from T , you move along to the next subarray. If you increment the pointer resulting from T[0] , you move along to the next int .

It might help you to look at a diagram of how a 2D array is laid out in memory compared to a dynamically allocated "2D array". A 3-by-3 2D array would look like this:

  0,0   0,1   0,2   1,0   1,1   1,2   2,0   2,1   2,2
┌─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┐
│ int │ int │ int │ int │ int │ int │ int │ int │ int │
└─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┘

Whereas if you dynamically allocated a 3-by-3 "2D array":

┌─────┐
│     │ // The int**
└──╂──┘
   ┃
   ▼
┌─────┬─────┬─────┐
│     │     │     ┿━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓  // An array of int*
└──╂──┴──╂──┴─────┘                            ┃
   ┃     ┗━━━━━━━━━━━━━━━┓                     ┃
   ▼                     ▼                     ▼
┌─────┬─────┬─────┐   ┌─────┬─────┬─────┐   ┌─────┬─────┬─────┐
│ int │ int │ int │   │ int │ int │ int │   │ int │ int │ int │ // Arrays of ints
└─────┴─────┴─────┘   └─────┴─────┴─────┘   └─────┴─────┴─────┘
  0,0   0,1   0,2       1,0   1,1   1,2       2,0   2,1   2,2

does the compiler remembers the dimension of the array and number of rows and columns?

Yes, if you have a variable with array type, the size of the array is part of that type. The compiler always knows the type of a variable.

And when asking for T[i][j] it actually asks for *(T+i*n+j) so this n is stored somewhere?

The expression T[i][j] is equivalent to *(*(T + i) + j) . Let's understand what this does. First, array-to-pointer conversion is undergone by T , giving an int (*)[m] . We then add i to this to move along to point at the i th subarray. This is then dereferenced to get the subarray. Next, this subarray also undergoes array-to-pointer conversion, giving an int* . You then add j to this to get a pointer to the j th int object in that subarray. This is dereferenced to give that int .

The problem is when we are trying to pass this thing (T) to a function. I dont know why but if n and m were constants its possible to pass T as a pointer to this array to function

It's actually a lie. You're not passing the 2D array to the function. In fact, there's no such thing as an array type argument. Your argument int K[][m] is actually equivalent to int (*K)[m] . That is, all array type arguments are transformed into pointers.

So when you call this function with add_5(T) , you're not passing the array denoted by T . T is actually undergoing array-to-pointer conversion to give you an int (*)[m] and this pointer is being passed into the function.

Here is an example of 2d dynamic array:

const int Mapsize = n

    int** GameField2 = 0;

GameField2 = new int*[Mapsize];                 // memory alocation
for(int i = 0; i < Mapsize; i++)
    GameField2[i] = new int[Mapsize];

for(int j = 0; j < Mapsize; j++)                
    for(int i = 0; i < Mapsize; i++)
        GameField2[i][j] = 0;

if you wish to operate on this array on any function, simply pass the pointer like this:

int Whatver(int** GameField)
{
    GameField[a][b]=x;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM