Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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 :)

like image 563
Vekta Avatar asked Dec 11 '22 22:12

Vekta


2 Answers

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".

like image 69
Borgleader Avatar answered Feb 02 '23 00:02

Borgleader


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 ]

like image 28
jogojapan Avatar answered Feb 01 '23 22:02

jogojapan