Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

3-D Arrays Using new

0 views
Skip to first unread message

Robert Penoyer

unread,
Jul 17, 2001, 3:55:39 PM7/17/01
to
I have a need for a fairly large 3-D array for the first time but I
don't know how to allocate space for it using new.

BCB5's Help offers something similar to this for a 2-D array:

int m = 3; // rows
int n = 5; // columns
int **data;

data = new int*[m]; // set up rows
for (int j = 0; j < m; j++) data[j] = new int[n]; // set up columns

I understand the first line as allocating m pointers and the second line
as allocating an n-integer pointer at each of the m pointers. BUT, I
don't see how to extend this thinking to 3 dimensions.

What is the correct way?
Also, how is the de-allocation handled?

Thanks,

Bob

Carlos Moreno

unread,
Jul 17, 2001, 5:06:46 PM7/17/01
to

Carlos Moreno wrote:
>
> Deallocation? [... and a bunch of buggy code followed ...]
>
> Needless to say that I'd advise you to use vectors instead :-)

And no, I was not talking about the error in my own code!
(those should be delete [] ;-))

Carlos
--

Carlos Moreno

unread,
Jul 17, 2001, 5:03:45 PM7/17/01
to

Before answering your question, may I suggest that you use vectors
instead? Your problem would be reduced to:

vector< vector <vector <int> > > matrix (10,10,10);
// IT DOES WORK (I'm talking about the 10,10,10), at
// least with recent versions of the compiler)

matrix[0][0][0] = 10;

// deallocation?? What is that? :-)


Now, if you want to do it manually, here's how it would work:

> BCB5's Help offers something similar to this for a 2-D array:
>
> int m = 3; // rows
> int n = 5; // columns
> int **data;
>
> data = new int*[m]; // set up rows
> for (int j = 0; j < m; j++) data[j] = new int[n]; // set up columns

You only have to realize that the above allocates an array of
arrays (data is a pointer to "int *"), but each of those "int *"s
is simply an array of integers.

So, what is a 3-D array?? It's an array of 2-D arrays:

// ALL OF THE CODE BELOW IS UNTESTED!

int *** data = new int ** [10];
for (int i = 0; i < 10; i++)
{
data[i] = new int *[20];
for (int j = 0; j < 20; j++)
{
data[i][j] = new int[30];
}
}

Now, you can use data[0][0][0] to data[9][19][29]

Deallocation? You have to deallocate each of the 10 2D-arrays
before deallocating data itself. Each of those 2D-arrays in turn
have to deallocate first the 20 1D-arrays before deallocating
their "main" pointer:

for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 20; j++)
{
delete data[i][j];
}
delete data[i];
}
delete data;


Needless to say that I'd advise you to use vectors instead :-)

Hope this helps,

Carlos
--

Jacques

unread,
Jul 17, 2001, 5:02:26 PM7/17/01
to
Allocation:
int x = 3;
int y = 4;
int z = 5;
int ***data;

data = new int**[x];
for(int i = 0; i < x; ++i)
{
data[i] = new int*[y];
for(int j = 0; j < y; ++j)
{
data[i][j] = new int[z];
}
}

Deallocation:
for(int i = 0; i < x; ++i)
{
for(int j = 0; j < y; ++j)


{
delete [] data[i][j];
}
delete [] data[i];
}
delete [] data;

David O'Neil

unread,
Jul 17, 2001, 6:36:13 PM7/17/01
to
Robert Penoyer wrote:
>
> I have a need for a fairly large 3-D array for the first time but I
> don't know how to allocate space for it using new.

Speak of the devil. We were discussing STL vs pointer and memory in another
thread, and in it I said that I wished when I was learning that someone had
pointed out the differences in the methods, so I will put my money where my
mouth is and tell you what I wished someone had told me in the beginning.

First, the example from Borland's help is very poorly written. If there is an
exception thrown during the creation of the collumns all of the memory
allocated for the rows** will be left allocated, and when the program
terminates if the OS doesn't free this memory the memory will be unusable
until the machine is rebooted. (Chances are the OS will free this memory, as
most modern OS's do this.)

Following is a program examining both raw pointer allocation of a
three-dimensional array, and an STL vector allocation of the same array. I do
not guarantee that either method correctly allocates the memory, as the 1)
pointers make my head hurt, and 2) the grammer of the vector makes my head
hurt :). But, they seem to work. (I am fairly confident that they are OK.
If any of the masters sees any mistakes please correct me.)

I have done my best to make both versions exception safe. When you understand
these you will begin to see why most people are preferring the STL. It is
MUCH quicker to write something in, and it is exception safe. In addition the
STL gives you many standard algorithms which can be used on the (vector, map,
list, ...).

Copy the following code into a blank console project. If you want, run the
System monitor along with it, and watch your memory being allocated.

Hope this helps (even though it is a lot to grok).
David


#include <conio.h>
#include <iostream>
#include <vector>

const int numRows = 100;
const int numCols = 100;
const int numPlanes = 100;

void testVector() {
using std::vector;
vector<vector<vector<int> > > array(numPlanes,
vector<vector<int> >(numCols, vector<int>(numRows)));
//Carlos' method does not work for me with BCB4.0
//vector<vector<vector<int> > > array(10, 10, 10);
std::cout << "\nMemory allocated - press any key"; getch();
}

int main() {
try {
std::cout << "Press a key to start"; getch();
int counter(0), y(0), z(0);
int*** array;
try {
array = new int**[numPlanes];
for (int plane=0; plane<numPlanes; ++plane, ++z) {
array[plane] = new int*[numCols];
for (int col=0; col<numCols; ++col, ++y) {
array[plane][col] = new int[numRows];
for (int row=0; row<numRows; ++row) {
array[plane][col][row] = counter++;
}
}
}
}
catch (...) {
for (int i=0; i<z; ++i) {
for (int j=0; j<y; ++j) {
delete[] array[i][j];
}
delete[] array[i];
}
delete[] array;
throw (1); //To bypass deallocation
}
std::cout << "\nArray created"; getch();
for (int plane=0; plane<numPlanes; ++plane) {
for (int col=0; col<numCols; ++col) {
delete[] array[plane][col]; //delete the rows
}
delete[] array[plane]; //delete the collumns
}
delete[] array; //delete the array
std::cout << "\nArray memory freed - press key to create vector";
getch();
}
catch (...) {
std::cout << "Allocation error - press any key to exit"; getch();
}
testVector();
std::cout << "\nVector memory freed - press key to quit"; getch();
}

Oscar Fuentes

unread,
Jul 17, 2001, 7:28:55 PM7/17/01
to
Carlos Moreno <mor...@mochima.com> writes:

> Before answering your question, may I suggest that you use vectors
> instead? Your problem would be reduced to:

[snip]

I agree that the vector solution is better that the pointer solution,
but why we can't go to the final? Multidimensional arrays are not a
'native' feature of C++, but we can exploit the language to overcome
this limitation.

Perhaps the OP is interested in trying a good matrix library. Out
there exists some that are extremely efficient and gives you the
syntax m[x][y][z], among other things.

Note that the vector and the pointer solution introduces the overhead
of intermediary pointers. A non-dynamic array represents itself
efficiently as a one-dimensional array (as we all know, everything is
represented as a one-dimension array in memory). A library can hide
you this implementation details, while you keep all the advantages of
a high level interface.

Cheers.

Robert Penoyer

unread,
Jul 17, 2001, 11:59:24 PM7/17/01
to
Thanks to everyone for their help!

Bob

Robert Penoyer

unread,
Jul 18, 2001, 12:01:51 AM7/18/01
to
Thanks very much for such a detailed response.

Bob

Hendrik Schober

unread,
Jul 18, 2001, 7:43:28 AM7/18/01
to
"David O'Neil" <david@anonymous_one.com> wrote:
> [...]

> try {
> std::cout << "Press a key to start"; getch();
> int counter(0), y(0), z(0);
> int*** array;
> try {
> array = new int**[numPlanes];
> for (int plane=0; plane<numPlanes; ++plane, ++z) {
> array[plane] = new int*[numCols];
> for (int col=0; col<numCols; ++col, ++y) {
> array[plane][col] = new int[numRows];
> for (int row=0; row<numRows; ++row) {
> array[plane][col][row] = counter++;
> }
> }
> }
> }
> catch (...) {
> for (int i=0; i<z; ++i) {
> for (int j=0; j<y; ++j) {
> delete[] array[i][j];
> }
> delete[] array[i];
> }
> delete[] array;
> throw (1); //To bypass deallocation
> }

So here, how come you assume everything allocated and
needs to be de-allocated? I don't see where an error
could come from except from calls to the new operator.
And if _that_ was the case, you mustn't blindly try to
delete _all_ memory. (Or am I missing something?)
A way to do this would be to NULLify all pointers first.
This sure shows, that raw pointers are a lot harder to
handle than 'vector's.
(And I see Ed shaking his head... ;^> )

> [...]

Schobi

--
Spam...@gmx.de is never read
I'm hschober at gmx dot de

P.S.: Ich suche einen Job in Berlin.


Hendrik Schober

unread,
Jul 18, 2001, 7:50:56 AM7/18/01
to
"Robert Penoyer" <rpen...@earthlink.net> wrote:
> I have a need for a fairly large 3-D array for the first time but I
> don't know how to allocate space for it using new.
>
> BCB5's Help offers something similar to this for a 2-D array:
> [...]

Did you realize that, whatever you do to build a
3D array, in memory it will always be layed out
in one dimension? You could do this yourself just
as well:

const std::size_t sizeDim1 = 2;
const std::size_t sizeDim2 = 3;
const std::size_t sizeDim3 = 4;

int* data = new int[sizeDim1*sizeDim2*sizeDim3];

int& access(int* data,
std::size_t dim1, std::size_t dim2, std::size_t dim3)
{
// Note: I didn't think this through, so there might be
// even conceptual flaws. But you might get the idea.
return data[ dim1*sizeDim2*sizeDim3
+ dim2*sizeDim3
+ dim3];
}

Now package this into a class, provide all the
common functions ('size()','begin()','end()'...)
and you'll have a nice little STL container to
use. (A co-worker of mine once implemented that
for 2D. Unfortunately I'm not allowed to post the
code.)

> Bob

David O'Neil

unread,
Jul 18, 2001, 9:37:44 AM7/18/01
to

Read the 'catch' block again. 'for (int i=0; i<z; ++i)'
^

The values 'z' and 'y' are incremented during the allocation of the memory, so
I simply reiterate over the values which have been allocated. This only
deallocates the memory which has been allocated. Setting them to NULL
initially and iterating through all of them would have been a little less
complex, I agree, but that is not the method my mind came up with as I was
programming. I suppose, as well, that I should have called 'y' 'allocRows',
and 'z' 'allocPlanes', and it would have been clearer. Sorry.

David

David O'Neil

unread,
Jul 18, 2001, 10:32:09 AM7/18/01
to
Hendrik Schober wrote:
>
...
> Did you realize that, whatever you do to build a
> 3D array, in memory it will always be layed out
> in one dimension?

The above statement is false. If you used your method to access an element in
the code I posted you would most likely access uninitialized memory at some
point. In order to set it up linearly you must code it yourself, as your
co-worker did.

The way I prefer to think (vs the explanation I have posted elsewhere) about the
method I posted may help explain the reason that these may not be laid out
linearly.

int*** array; //create pointer to pointer to pointer
//...
array = new int**[numPlanes]; //Allocate an array of pointers to hold the
//pointers to pointers to the beginning of
//each plane
//...
array[plane] = new int*[numCols]; //Allocate an array of pointers to hold
//the pointers to the beginning of each
//collumn in the plane
//...
array[plane][col] = new int[numRows]; //Finally! Allocate the memory for
//each individual row of the array

As you can see, where each individual collumn is located in memory is intimately
tied to where the memory manager allocates it to be, and there is no guarantee
that the memory manager will allocate these linearly. The second collumn may
actually be placed BEFORE the first collumn. If your statement was correct the
following (non-safe) code should work (unless an exception is thrown :) ), but,
after it prints out the first 3 values of the linear representation it faults on
my machine, because they are NOT linearly stored.

Happy coding,
David


#include <conio.h>
#include <iostream>
#include <vector>

const int numRows = 3;
const int numCols = 3;
const int numPlanes = 3;

int main() {
int*** array;


array = new int**[numPlanes];

for (int plane=0; plane<numPlanes; ++plane) {


array[plane] = new int*[numCols];

for (int col=0; col<numCols; ++col) {


array[plane][col] = new int[numRows];
}
}

int offset(0);
for (int z=0; z<numPlanes; ++z)
for (int y=0; y<numRows; ++y)
for (int x=0; x<numCols; ++x)
array[x][y][z] = offset++;

for (int z=0; z<numPlanes; ++z) {
for (int y=0; y<numRows; ++y) {
for (int x=0; x<numCols; ++x) {
std::cout << array[x][y][z] << ", ";
}
std::cout << "\n";
}
std::cout << "\n";
}
std::cout << "\nPress a key\n\n"; getch();
offset = 0;
for (int z=0; z<numPlanes; ++z) {
for (int y=0; y<numRows; ++y) {
for (int x=0; x<numCols; ++x) {
std::cout << **array[offset++] << ", "; //Faults after 3 values
}
std::cout << "\n";
}
std::cout << "\n";
}
std::cout << "\nPress a key"; getch();


for (int plane=0; plane<numPlanes; ++plane) {
for (int col=0; col<numCols; ++col) {
delete[] array[plane][col]; //delete the rows
}
delete[] array[plane]; //delete the collumns
}
delete[] array; //delete the array

return 0;
}

Hendrik Schober

unread,
Jul 18, 2001, 11:25:29 AM7/18/01
to
"David O'Neil" <david@anonymous_one.com> wrote:
> [...]
> Read the 'catch' block again. 'for (int i=0; i<z; ++i)'

Of course! Stupid me. Never mind...

> David

Hendrik Schober

unread,
Jul 18, 2001, 11:37:04 AM7/18/01
to
"David O'Neil" <david@anonymous_one.com> wrote:
> Hendrik Schober wrote:
> >
> ...
> > Did you realize that, whatever you do to build a
> > 3D array, in memory it will always be layed out
> > in one dimension?
>
> The above statement is false.

So does your RAM's address space have more than one
dimension? ;^> I don't believe you.

> If you used your method to access an element in
> the code I posted you would most likely access uninitialized memory at some
> point.

Of course. I didn't write down how to init it.
But that doesn't render the idea useless.

> [...]


> As you can see, where each individual collumn is located in memory is intimately
> tied to where the memory manager allocates it to be, and there is no guarantee
> that the memory manager will allocate these linearly. The second collumn may
> actually be placed BEFORE the first collumn.

Oh, now I see where we misunderstood. Well, I
didn't say, it would be layed out all in one big
chunk, no matter how you did. Instead I tried to
say, it would always be within one dimension.
That's based on the assumption that everybody
uses RAM with a linear addressing.

You can, however, allocate all the neccessary
memory in one big chunk and perform the abstraction
of breaking it into several dimensions yourself.
When you do this, your memory will be laid out in
one big chunk. (Note: This can be an advantage.
For example, it could probably avoid paging,
improve cashing etc.)

> [...]
> David

Schobi

> [...]

0 new messages