0

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?

5
  • If you "completely understand" the subject, why do you want us to "check if I'm right"? Commented May 5, 2013 at 22:59
  • 3
    "because in C++ an array is in fact a pointer" That's the mistake right there. Arrays are not pointers. Commented May 5, 2013 at 22:59
  • What I meant when we create an array the most straight forward method what we get is a pointer solely (C++ allocated memory on the side so nothing "really" connencts this pointer to this block of data exept it's value is the address of first cell. We can change the value of poiter as well. Commented May 5, 2013 at 23:04
  • By saying "I completely understand.." (read the rest) and then writing "check if I'm right" i wanted to show you that I'm right. Like when you say "Hey look I'm strong" and then lift a heavy object to prove someone that.. Commented May 5, 2013 at 23:06
  • @MichałKownacki "when we create an array the most straight forward method what we get is a pointer solely" - That's where you're wrong. You don't get a pointer at all. You get an array. Arrays and pointers are not the same. Commented May 5, 2013 at 23:21

2 Answers 2

5

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 ints". 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 ith 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 jth 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.

Sign up to request clarification or add additional context in comments.

6 Comments

Ok, so arrays are not pointer in C++ but we have a conversion from array "object" to a pointer. I got this. Thank you. So why if in the shown program I HAVE TO make n and m const? And how to avoid this and in the same time have and array declared like: int T[n][m] ; ? And pass such 2D array to a function (C++ should remember the dimension or should we pass the as arguments?)?
@MichalKownacki The reason n and m have to be const is a different issue. In C++, types are entirely known at compile-time (it's a statically typed language). Since the size of an array is part of its type, it must also be known at compile-time. The expression giving the size of an array must be what is called a constant-expression (can be evaluated at compile-time). A const int can be used as a constant expression. A non-const int can't, because it can change at run-time.
@MichałKownacki If you need a dynamically sized array, you need to dynamically allocate. However, it is preferred that you use a standard container that encapsulates the allocation for you, such as std::vector.
I see. You wrote "the size of an array is part of its type". So please tell me what exactly is stored in program at pre-compile time, when creating int T[n][m]? I noticed only m is required to be constant. So when creating int T[n][m] T is of type int (*)[m] ?
Please answer my last comment :)
|
0

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;
}

1 Comment

OK, I know we can do something like this. But doeas C++ allows to pass an array to a function if array was delared like this: int T[n][m] ?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.