PDA

View Full Version : multi-dimensional arrays


flipflop
2002.05.30, 01:59 PM
I'm sorry if this is a stupid question but it has me stumped! :p

I define a multi-dimensional array in one class:

@interface foo : NSObject
{
int tiles[20][18];
}

// I know this doesn't work...
- (int)getTilesArray;

and want to return the array via a message. What would the message signature look like? I would like to do the following:

- (int)getTilesArray
{
return tiles;
}

... so I can do something like this in another object (assuming foo was initialized properly of course):

int myTiles[20][18];
myTiles = [foo getTilesArray];
myTiles[10][3] = 14;

Thanks for your help,
- John :)

sinclair44
2002.05.30, 02:30 PM
I could be completely off, but I THINK this might work:


- (int)getTilesArray;

to

- (int*)getTilesArray;


Then again, I'm not an extremly experienced programmer, so you may want to try it and still wait for a better answer.

flipflop
2002.05.30, 02:46 PM
Here are some things i have tried with no success:

-(int *)getTilesArray
-(int [][])getTilesArray
-(int [20][]) getTilesArray

- John

OneSadCookie
2002.05.30, 03:26 PM
You can't do it so simply. You can't assign a whole array like that, you have to use pointers.

What I would do in your situation is to make the 2D array one-dimensional, so int array[20][8] becomes int array[20 * 8], and array[y][x] becomes array[x + (y * 8)]. Then you can change your method to return an int*, and the variable you're assigning to to be an int*, and things should pretty-much work.

The other option is to make a 2D array using multiple 1D arrays, so int array[20][8] becomes
int** array = (int*)malloc(20 * sizeof(int*));
unsigned int i = 0;
for (i = 0; i < 20; ++i)
{
array[i] = (int)malloc(8 * sizeof(int));
}
and your method returns int** and variable is of type int**. This has the advantage that normal 2D array indexing works as expected.

GoodDoug
2002.05.30, 04:26 PM
Ahh once again I get to expound on the evils of 2d arrays.


int foo[100][100];
// this would seem to allocate 100 pointers to int arrays (ie pointers to pointers)
// Most compilers have tiny fits
// when they see this

// this next is the "correct" way of doing what you suggest
int ** foo;
foo = (int**)calloc(100, sizeof(int*));

for (i = 1 to 100)
{
foo[i] = (int*)calloc(100, sizeof(int));
}
// This really kills spatial coherency and can really slow down either
// sequential or even random access, as the number you need next
// won't be in the cache line most of the time.

// The way I do it...
int foo[100*100];
// to index correctly
x = foo[(row*100) + column];
// A little more difficult to debug, but much better to use

Now, to answer your actual question:
"tiles" is an int**, not an int*.

so, - (int**)tilesArray; is the correct selector signature for what you want to do... however, I would consider abstracting that out even further as such:

@interface foo : NSObject
{
int tiles[20*18];
}

- (int) getTileAtRow:(int)inRow column:(int)inColumn
{
if (inRow >= 20 || inColumn >= 18)
return -1; // or do an NSAssert here

return tiles[(inRow * 20) + inColumn];
}

// to do something like you want (copying to a new object...)
// call it copy so that you know that you are allocating new
// memory that you will be responsible for freeing later
- (int*) copyTilesArray
{
int* newCopyArray = calloc(20*18);
memcpy(newCopyArray, tiles, sizeof(int) * 20 * 18);
return newCopyArray;
}


The nice thing about this is that if you need to change the underlying mechanism (from a straight array of ints to some STL container even to an NSArray of NSDcitionarys) Your interface to it doesn't have to change.

jefftkd
2002.05.30, 04:27 PM
To do it easily, just pass the pointer as an argument (instead of returning it).


/* Declared somewhere in your class: */
int myTiles[width][height];

- (BOOL)getTiles: (int*)tiles bufferSize: (int)size
{
if (size < sizeof(myTiles))
{
NSLog(@"Buffer size to -getTiles was too small!");
return NO;
}

tiles = &myTiles[0][0];
return YES;
}

HTH,
Jeff :cool:

OneSadCookie
2002.05.30, 04:47 PM
Actually, int[X][Y] is of type int[X][Y], no pointers involved. The memory used is precisely X * Y * sizeof(int). It's in no way compatible with the second method I mentioned (the int** way), as Jeff is suggesting. Basically, it's very hard to pass one of these around.

I've never met a compiler that had any problems with these, except that they're sometimes a little too large.

flipflop
2002.05.30, 06:16 PM
I like the int ** sollution as this will allow us to continue using existing code which uses indexing heavily. The drawback to this is using (IMHO) primitive methods like malloc or calloc instead of objects. Sure objects have more overhead but does it really matter with todays hardware and compilers?

This above method works but I do get 2 compile time warning messages using the following code:


int **array = (int*)malloc(kNumColumns * sizeof(int*));
for (i = 0; i < kNumColumns; ++i) {
array[i] = (int)malloc(kNumRows * sizeof(int));
}


warning: initialization from incompatible pointer type
warning: assignment makes pointer from integer without a cast

I'm toying with the idea of using NSMutableArray. Similar to what has been discussed above but with the added benifit of using objects.

NSMutableArray *foo;
foo = [NSMutableArray arrayWithCapacity: (numColumns * numRows)];
int x = [foo objectAtIndex: (row * numRows) + column];

Now we can simply pass an object arround, much easier then passing arrays. :) It would be nice to be able to pass in a 2D array to NSMutableArray and have it copied into the object. Sounds like a job for a category...

Thanks for all your help!
- John :)

OneSadCookie
2002.05.30, 06:29 PM
Oops, code should be:


int **array = (int**)malloc(kNumColumns * sizeof(int*));
for (i = 0; i < kNumColumns; ++i) {
array[i] = (int*)malloc(kNumRows * sizeof(int));
}


Note the extra *s in the casts.

flipflop
2002.05.30, 06:44 PM
Eeek! Scary stuff when the previous code compiles and runs with only a warning to hint that something is serriously wrong. Thank goodness for protected memory and for people that understand pointers! :)

One more question -- What would the code snipit be to release all the memory allocated using the above method?

Thanks,
- John

jefftkd
2002.05.31, 10:53 AM
int **array = (int**)malloc(kNumColumns * sizeof(int*));
for (i = 0; i < kNumColumns; ++i) {
array[ i ] = (int*)malloc(kNumRows * sizeof(int));
}
To free would be:


for(i = 0;i < kNumColumns;i++){
if (array[ i ] != NULL) free(array[ i ]);
}
free(array);
Note also, even though you may dislike using primitives over objects, they are MUCH faster!

HTH,
Jeff :cool:

P.S. To board people: It takes me several tries to write code in here, because array[ i ] (note: spaces) turns into italics, and when writing Obj-C code I get :( everywhere. Could we please turn off enhancements inside code sections?

Josh
2002.05.31, 01:39 PM
Down there under "Options" you can disable the smilies I don't know of any ways to disable the vB code. Griggs, could this be a new option?

OneSadCookie
2002.05.31, 06:26 PM
You don't need to check for NULL before you free, free makes that check for you.

It's weird, smileys work inside CODE tags, but the VB code doesn't seem to (I couldn't get it to bold the extra stars before).

flipflop
2002.05.31, 09:03 PM
Just a quick note thanking everyone for their help. I'm now happily passing arrays around in my code and things can't get any better then that. OK, they can but at least I can enjoy the weekend having solved the problem. ;)

Thanks again and have a great weekend!
- John