gbadev.org forum archive

This is a read-only mirror of the content originally found on forum.gbadev.org (now offline), salvaged from Wayback machine copies. A new forum can be found here.

C/C++ > 2d array on the heap - noob question - answered :)

#143698 - mr_munk - Thu Oct 25, 2007 12:22 pm

Hi,

I Google'd the above and found this solution (which compiles and seems to function fine) but I don't understand what it is actually doing. Could someone fill me in ?

Code:
int (*my_array)[256] = new int[192][256];


Am I right in thinking that my_array is an array of pointers ?

If the size of my_array doesn't match the number of rows in the 2d array allocated on the heap this throws a compiler error - why is that ?


Last edited by mr_munk on Fri Oct 26, 2007 11:44 am; edited 3 times in total

#143700 - kusma - Thu Oct 25, 2007 12:48 pm

Yeah, it's an array of pointers - not what you wanted, eyh? :)
I usually use normal 1D arrays instead, and calculate the index using a multiplication.

Code:
int *my_array = new int[192 * 256];

x = 100;
y = 50;
my_array[x + y * 192] = 0xFF00FF;

#143701 - mr_munk - Thu Oct 25, 2007 1:00 pm

Thanks for the reply, I am still curious as to how this line of code works though - in particular how do the 256 pointers in my_array in the stack (I think?) become initialised with the addresses of 256 new 2d arrays in the heap?

#143717 - eKid - Thu Oct 25, 2007 3:41 pm

Code:
int (*my_array)[256] = new int[192][256];


this is kind of confusing
what i think it is (dont take my answer very seriously):

"my_array" is declared as 256 int pointers

" = new int[192][256];" i guess this allocates each pointer (256 of them) with 192 int entries

#143718 - mr_munk - Thu Oct 25, 2007 4:10 pm

Thanks for your reply, I wonder why the 2nd dimension of the 2d array on the heap has to match the size of the array on the stack ? If the 256 pointers overlay 256 arrays of 196*int each, I would have expected int[256]=new int[256][192] rather than the other way around.

#143721 - Lick - Thu Oct 25, 2007 4:29 pm

I think you should do this:
Code:
int **arr_2d = new int[y][x];


Note that y is the first index, x is the second. You can think of this as the screen is being divided into scanlines.
_________________
http://licklick.wordpress.com

#143723 - mr_munk - Thu Oct 25, 2007 5:04 pm

Thanks Lick, that code makes sense, although it throws a compiler error for me:

Code:
error: cannot convert 'uint (*)[256]' to 'uint**' in initialization


generated from:

Code:
uint **lifeBfr = new uint[192][256];//Life buffer

#143735 - sajiimori - Thu Oct 25, 2007 7:04 pm

Does this help?
Code:
typedef int Pixel;

// A scanline is 256 pixels.
typedef Pixel Scanline[256];

// A framebuffer is 192 scanlines.
typedef Scanline FrameBuffer[192];

// A whole 256x192 framebuffer, by value.
FrameBuffer fbByValue;
Pixel p = fbByValue[line][column];

// A framebuffer on the heap.
// Using it requires an extra dereference.
FrameBuffer* fbOnHeap = new FrameBuffer;
Pixel p = (*fbOnHeap)[line][column];

#143738 - Lick - Thu Oct 25, 2007 7:27 pm

mr_munk wrote:
Thanks Lick, that code makes sense, although it throws a compiler error for me:

Code:
error: cannot convert 'uint (*)[256]' to 'uint**' in initialization


generated from:

Code:
uint **lifeBfr = new uint[192][256];//Life buffer


My bad. I forgot you had to loop through the "outer array" to new the "inner arrays".
Code:
int **arr_2d = new (int *)[192];

for (i=0; i<192; i++) {
  arr_2d[0] = new int[256];
}


sajimori: that code is a genius way to explain why 192 goes first.
_________________
http://licklick.wordpress.com

#143747 - sajiimori - Thu Oct 25, 2007 8:22 pm

Note that the difference between my example and the single 1D array approach is merely syntactic: they are both contiguous blocks of memory.

On the other hand, the loop in Lick's previous example does 192 separate heap allocations, which is overly complex and inefficient.

#143752 - mr_munk - Thu Oct 25, 2007 9:09 pm

Thanks for all the help guys, I'm going to take some time to digest it properly.

<edit>

I'm going to hack on this problem for a while rather than bumping this thread, since I don't want to outstay my welcome ;)

Sajimori, just so you know, when I try to use typedef to implement a 2d array the following happens.

Code:
typedef int Pixel;
typedef Pixel ScanLine[256];
typedef ScanLine FrameBuffer[192];


generates the following compiler error:

Code:
error: cannot convert 'Pixel (*)[256]' to 'Pixel (*)[192][256]' in initialization

#143771 - Lick - Thu Oct 25, 2007 10:27 pm

Code:
typedef int Pixel;
typedef Pixel[256] ScanLine;
typedef ScanLine[192] FrameBuffer;


Does this work?
_________________
http://licklick.wordpress.com

#143773 - sajiimori - Thu Oct 25, 2007 10:32 pm

The typedefs aren't generating that error. If you'd like to paste the line specified by the complier error, I might be able to spot the problem. At a glance, it looks like it's trying to convert from a Scanline pointer to a FrameBuffer pointer.

But it's possible that I've made a mistake! I don't often use arrays in complicated ways, partly due to their second-class status in C++.

Structs/classes have first-class status, so I'm inclined to quickly wrap arrays in structs to make them behave more like regular values. For instance, you can't pass an array by value, but you can pass a struct by value even if it contains an array.

Edit: Lick, that syntax is incorrect. :) Write typedefs like variable declarations, and put the name of the new type where the variable name would go.

#143778 - Lick - Thu Oct 25, 2007 10:55 pm

Oh okay, yeah I lost it. Just recently I started writing C code again. =/
_________________
http://licklick.wordpress.com

#143824 - mr_munk - Fri Oct 26, 2007 11:43 am

Well, I now have two versions of my code (which is a 'game of life engine' that I am trying to optimise, BTW:) Oh! and this isn't coursework - I'm not at University - I want to use it as part of a game.

The first uses 1d arrays to hold the current and next generation of cells, this benefits from easy use of memcpy (since the array on the heap only uses one pointer) but also takes a performance hit from the extra math to simulate 2d references (partially replaced by LUTS but still to slower than the second.)

The second program uses 2d arrays which makes the code more efficient and elegant (esp. in regards to using LUT's for modulo functions) but it is hard to figure out a valid memcpy call for the 2d arrays (since there structure seems to be an array of pointers to arrays and each row array has 2 pointers.)

Sajimori - I am a relative beginner, so I am sure it is I who has made the mistake ;) For completeness here is the declaration which the error refers to:

Code:
FrameBuffer* fbOnHeap = new FrameBuffer;


Thanks to all - I have learned quite a bit in the last day or so thanks to your support :) I will declare this question answered.

<edit>

Thanks to Sajimori's comment about the memory layout being contiguous I figured out the (simple) answer to memCpy the 2d arrays. D'oh.

#143870 - sajiimori - Fri Oct 26, 2007 11:46 pm

Nope, it was my mistake after all! I don't know why it doesn't work, but arrays are truly weird and incongruous with the rest of C++ so I'd just do this:
Code:
struct FrameBuffer { ScanLine lines[192]; };
FrameBuffer* fb = new FrameBuffer;

I compiled that and it works fine.