简体   繁体   中英

Multidimensional dynamic arrays, Why doesn't it work?

I'm not looking for a solution here, I've found plenty on Google. I'm looking for an explanation.

While playing with arrays I found that declaring a 2D array dynamically doesn't work like expected

1D Array, Works

int main()
{
int rows;
int* pointer;
pointer = new int[rows]; 
}

2D Array, Doesn't Work

int main()
{
int rows;
int columns;
int* pointer;
pointer = new int[rows][columns]; //error on this line
}

This seems to me like the intuitive way of doing things because that's how its done with regular arrays, but apparently its incorrect and won't compile.

I haven't been able to find a clear explanation of WHY this is the case, hopefully someone here can enlighten me.

Thanks :)

The reason this doesnt work is because to be able to have a two dimensional array you need one array of pointers which will point to many arrays of the type you want.

In this case you're trying to store the address to an array of int* (of which each "cell" will point to an array of ints) in a variable of type int*, it should be int**.

To recap: 1D array: int* 2D array: int** 3D array: int***

The reason all arrays must be one dimensional is because your memory is one dimensional (think of all the memory as being one big array of memory addresses) which means multiple dimension arrays must be "faked".

This is not only a problem of the data type as described by the other answers.

A syntax like

pointer = new int[rows][columns]

is only valid if columns is a constant expression. You can't use a variable there (but note that rows can be a variable).

Here is the explanation. The C++ Standard enables you to use two-dimensional-array syntax with the new operator in the following way only (from §5.3.4/1):

new-expression:
  ::opt new new-placementopt new-type-id new-initializeropt
  ::opt new new-placementopt ( type-id ) new-initializeropt
new-placement:
  ( expression-list )
new-type-id:
  type-specifier-seq new-declaratoropt
new-declarator:
  ptr-operator new-declaratoropt
  noptr-new-declarator
noptr-new-declarator:
  [ expression ] attribute-specifier-seqopt
  noptr-new-declarator [ constant-expression ] attribute-specifier-seqopt
new-initializer:
  ( expression-listopt )
  braced-init-list

The relevant part is this:

noptr-new-declarator:
  [ expression ] attribute-specifier-seqopt
  noptr-new-declarator [ constant-expression ] attribute-specifier-seqopt

The first line means you can have one bracketed expression after the type-id. The second line (which is a recursive statement) allows you to use multiple pairs of brackets after the initial one, but they must enclose a constant expression .

The Standard explains this further (emphasis mine):

(§5.3.4/5) When the allocated object is an array (that is, the noptr-new-declarator syntax is used or the new-type-id or type-id denotes an array type), the new-expression yields a pointer to the initial element (if any) of the array. [ Note: both new int and new int[10] have type int* and the type of new int[i][10] is int (*)[10] — end note ] The attribute-specifier-seq in a noptr-new-declarator appertains to the associated array type.

(§5.3.4/6) Every constant-expression in a noptr-new-declarator shall be an integral constant expression (5.19) and evaluate to a strictly positive value. The expression in a noptr-new-declarator shall be of integral type, unscoped enumeration type, or a class type for which a single non-explicit conversion function to integral or unscoped enumeration type exists (12.3). [...] [ Example: Given the definition int n = 42, new float[n][5] is well-formed (because n is the expression of a noptr-new-declarator), but new float[5][n] is ill-formed (because n is not a constant expression). — end example ]

Well you can understand it in following manner:

A 2-D array looks like a grid with row and columns for human representation but in memory it is stored in contiguous memory. So whenever you say a 2-D array or size mxn you are talking about m arrays of n elements each stored one after another. ie for a 3x3 grid elements are stored as :

(0,0) (0,1) (0,2) (1,0) (1,1) (1,2) (2,0) (2,1) (2,2)

If you want to access such data structure you need to use pointer to pointer ie a pointer to an array of addresses of location of the first element of every row.

So the array can be accessed by means of 3 (number of rows) addresses

(Address of element 0,1) (Address of element 1,0) (Address of element 2,0)

Since address of elements are stored so now you need a pointer to pointer to access this array of pointers (holding addresses of first element of every row).

For 1D array:

int* pointer = new int[3];

Array: (0) (1) (2)
pointer: (Address of first element of Array)

For a 2D array:

int **pointer = new int[3][3];

Array: (0,0) (0,1) (0,2) (1,0) (1,1) (1,2) (2,0) (2,1) (2,2)
Row Address Array: (Address of 0,1) (Address of 1,0) (Address of 2,0)
pointer: (Address of first element of Row Address Array)

Hope this helps!

Other people have explained it well, but here is an example that may shed light on creating dynamically sized multi-dimensional arrays using new :

int rows = 20;
int columns = 30;

// the following is essentially just an array of int pointers
int **pointer = new int*[rows]; 

// so, loop through the array creating the second dimension
for (int i = 0;i < rows;i++)
    pointer[i] = new int[columns];

This is a one dimensional char pointer array : char* Dynamic_One_Dimensional_Char_Pointer_Array . Notice the char * .

This is a two dimensional char pointer array : char **Dynamic_Two_Dimensional_Char_Pointer_Array . Notice the char ** .

This is how you allocate memory for aa two dimensional char pointer array :

    //memory allocated for elements of rows.
    Dynamic_Two_Dimensional_Char_Pointer_Array = new char *[ROWS] ;

    //memory allocated for  elements of each column.
    for(   i = 0 ; i < ROWS ; i++ ) Dynamic_Two_Dimensional_Char_Pointer_Array[i] = new char[COLUMNS];

This is how to print the 2D array ( [rows][columns] ) :

   for(rows=0;rows<maxLines;rows++)
   {
       for(int columns=0;columns<strlen(Dynamic_Two_Dimensional_Char_Pointer_Array[rows]);columns++)
       {
            cout<<Dynamic_Two_Dimensional_Char_Pointer_Array[rows][columns]<<"";
       }
       cout<<"  \n";
   }

his is how you de-allocate memory for aa two dimensional char pointer array :

//free the allocated memory
for(   i = 0 ; i < ROWS ; i++ )   delete [] Dynamic_Two_Dimensional_Char_Pointer_Array[i] ;
delete [] Dynamic_Two_Dimensional_Char_Pointer_Array ;

Full sample code:

#include<vector>
#include<string>
#include<iostream>
using namespace std;

int main()
{

    int COLUMNS =80; //80 characters wide
    int ROWS =20;// 20 lines
    int i,maxLines=0;

    char* Dynamic_One_Dimensional_Char_Pointer_Array = new char[80];
    char **Dynamic_Two_Dimensional_Char_Pointer_Array = 0;

    //memory allocated for elements of rows.
    Dynamic_Two_Dimensional_Char_Pointer_Array = new char *[ROWS] ;

    //memory allocated for  elements of each column.
    for(   i = 0 ; i < ROWS ; i++ ) Dynamic_Two_Dimensional_Char_Pointer_Array[i] = new char[COLUMNS];


    strcpy(Dynamic_One_Dimensional_Char_Pointer_Array,"apples 123 oranges 456 bananas 789 lemons 101112 kiwi 132415 grapes 161718" );
    cout<<"  \ninput = "<<Dynamic_One_Dimensional_Char_Pointer_Array<<"  \n\n";
    cout<<"Output = \n";

    char seperators[]   = " ,\t\n";
    char *token; 
   token = strtok( Dynamic_One_Dimensional_Char_Pointer_Array, seperators );  
   i=0;

   while( token != NULL )
   {
      strcpy(Dynamic_Two_Dimensional_Char_Pointer_Array[i],token);
      token = strtok( NULL, seperators );  
      i++;
   }
   maxLines=i;
   cout<<"  \n";

   cout<<"show contents 1 print [rows] \n";
   cout<<"-------------------------------------------\n";
   for(int rows=0;rows<maxLines;rows++)
   {
     cout<<Dynamic_Two_Dimensional_Char_Pointer_Array[rows]<<"  \n";
   }
   cout<<"  \n";

   cout<<"show contents 2  print [rows][columns]\n";
   cout<<"-------------------------------------------\n";

   for(rows=0;rows<maxLines;rows++)
   {
     //cout<<Dynamic_Two_Dimensional_Char_Pointer_Array[rows]<<"  \n";
       for(int columns=0;columns<strlen(Dynamic_Two_Dimensional_Char_Pointer_Array[rows]);columns++)
       {
            cout<<Dynamic_Two_Dimensional_Char_Pointer_Array[rows][columns]<<"";
       }
       cout<<"  \n";
   }



    delete[] Dynamic_One_Dimensional_Char_Pointer_Array;

    //free the allocated memory
    for(   i = 0 ; i < ROWS ; i++ )   delete [] Dynamic_Two_Dimensional_Char_Pointer_Array[i] ;
    delete [] Dynamic_Two_Dimensional_Char_Pointer_Array ;

    return 0;
}

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