PDA

View Full Version : typedef, multi-dimensional arrays, and C




wrldwzrd89
Sep 27, 2008, 06:24 PM
Is it possible to define a custom type that represents a multi-dimensional array of some arbitrary type?

Perhaps an example will help.


/* testing.h */

/* constants */
#define ARRAY_SIZE 10

/* custom types */
typedef unsigned char byte;
typedef byte ARRAYONE[ARRAY_SIZE][ARRAY_SIZE]; /* doesn't work! */
typedef byte[ARRAY_SIZE][ARRAY_SIZE] ARRAYTWO; /* also doesn't work! */
typedef byte ARRAYTHREEROW[ARRAY_SIZE]; /* doesn't work either */
typedef ARRAYTHREEROW ARRAYTHREE[ARRAY_SIZE]; /* again fails to work */


The compiler I'm using (Xcode 3.1) issues an error message on all 4 of the typedef statements.



HiRez
Sep 27, 2008, 10:03 PM
What project type and language variant (C, Obj-C, C++) are you using? And what are the errors is it giving you? When I put your code into a plain ANSI C Xcode project ("Standard Tool"), the only typedef that gives me an error is

typedef byte[ARRAY_SIZE][ARRAY_SIZE] ARRAYTWO; /* also doesn't work! */

The error is "error: syntax error before '[' token"

The others seem to compile fine.

lee1210
Sep 27, 2008, 10:11 PM
As HiRez stated, most of what you were doing was ok in C (and hence objective-C). I was able to compile my example below with gcc and g++, so it should be OK in C++, too. Were you getting some sort of warnings or were these really errors?

I wrote some test code, and this seems to work A-OK:

#include <stdio.h>
#define DIM1 10
#define DIM2 100
int main(int argc, char *argv[]) {
typedef unsigned char byteList[DIM1][DIM2];
typedef unsigned char byte;
typedef byte byteList2[DIM1][DIM2];
byteList myList;
byteList2 myList2;
myList[1][12]=0x7;
myList[2][15]=0x9;
myList[1][12]=0x1;
myList[2][15]=0x4;
return 0;
}


-Lee

Edit: Note this is not of "some arbitrary type", it is of a specific type, a bunch of unsigned chars. The closest one might get to such a thing in C would be a void * array, but casting gets messy, you need nice, unique memory locations to hold the values, then you can store the pointers to those things cast to void *, then cast them back to, say, int * or float * when they come out.

Edit 2: Here's an example of having a list that can hold generic types of things. The init method I wrote assumes that once you have a list, it will hold the same sort of thing. This isn't necessary, but things will get even worse than it already is in the example. The helper functions to set and get elements are to avoid particularly messing looking things like:

*(double *)myDoubleList[1][6];


So here it is:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define Dim1 20
#define Dim2 150

typedef void *genericList[Dim1][Dim2];
void initGenericList(genericList,size_t);
void freeGenericList(genericList);
void setIntElement(void *,int);
void setFloatElement(void *,float);
void setDoubleElement(void *,double);
void setCharElement(void *,char);
int getIntElement(void *);
double getDoubleElement(void *);
char getCharElement(void *);

int main(int argc, char *argv[]) {
genericList myIntList,myCharList,myDoubleList;
double myDouble=0.;
char myChar='\0';
int myInt=0;

initGenericList(myIntList,sizeof(int));
initGenericList(myCharList,sizeof(char));
initGenericList(myDoubleList,sizeof(double));

setIntElement(myIntList[2][4],5);
setDoubleElement(myDoubleList[1][6],6.2);
setCharElement(myCharList[5][2],'a');

myInt=getIntElement(myIntList[2][4]);
myChar=getCharElement(myCharList[5][2]);
myDouble=getDoubleElement(myDoubleList[1][6]);
printf("The values are: %d\t%c\t%lf\n",myInt,myChar,myDouble);

myInt=getIntElement(myIntList[0][0]);
myChar=getCharElement(myCharList[0][0]);
myDouble=getDoubleElement(myDoubleList[0][0]);
printf("The values are: %d\t%c\t%lf\n",myInt,myChar,myDouble);

freeGenericList(myIntList);
freeGenericList(myCharList);
freeGenericList(myDoubleList);

return 0;
}

void initGenericList(genericList toInit,size_t elementSize) {
int x = 0;
int y = 0;
void *tmpPtr = NULL;
for(x = 0; x < Dim1; x++) {
for(y = 0; y < Dim2; y++) {
tmpPtr = malloc(elementSize);
bzero(tmpPtr,elementSize);
toInit[x][y]=tmpPtr;
}
}
}

void freeGenericList(genericList toFree) {
int x = 0;
int y = 0;
for(x = 0; x < Dim1; x++) {
for(y = 0; y < Dim2; y++) {
free(toFree[x][y]);
}
}
}

void setIntElement(void *toSet,int val){
*(int *)toSet=val;
}

void setFloatElement(void *toSet,float val){
*(float *)toSet=val;
}

void setDoubleElement(void *toSet,double val){
*(double *)toSet=val;
}

void setCharElement(void *toSet,char val){
*(char *)toSet=val;
}

char getCharElement(void *toGet) {
return *(char *)toGet;
}

float getFloatElement(void *toGet) {
return *(float *)toGet;
}

int getIntElement(void *toGet) {
return *(int *)toGet;
}

double getDoubleElement(void *toGet) {
return *(double *)toGet;
}


By posting this, i do not endorse its use, but you said "some arbitrary" type, which a unsigned char is not. This will also work with structs, but I didn't write the helper functions. You can still just pass the sizeof(type) for initialization. This could also store other pointers (to primitives, structs, functions, etc.), again, i didn't write helper functions for such things, but it wouldn't be too hard. Again, I am not endorsing its use, but... there it is.

-Lee

wrldwzrd89
Sep 28, 2008, 12:28 AM
What project type and language variant (C, Obj-C, C++) are you using? And what are the errors is it giving you? When I put your code into a plain ANSI C Xcode project ("Standard Tool"), the only typedef that gives me an error is

typedef byte[ARRAY_SIZE][ARRAY_SIZE] ARRAYTWO; /* also doesn't work! */The error is "error: syntax error before '[' token"

The others seem to compile fine.
I'm using "BSD Static Library" as the project type, and using plain C.
The error I'm getting is: syntax error before ; token

Oddly enough, the example code I gave you compiles just fine, except for the second typedef statement, which gives a syntax error before [ token.

...but this other example does NOT compile, giving the error I mentioned above.
#define MAP_SIZE 255;

typedef unsigned char MAP_CELL;
typedef MAP_CELL MAP[MAP_SIZE][MAP_SIZE];It looks identical to the first example, but gcc doesn't like it for some odd reason.

lee1210
Sep 28, 2008, 12:44 AM
#define has led you astray. You had it right in the original.

That's just a pre-processor directive. Any instance of the first item is replaced with the second.

So:

#define MAP_SIZE 255;

typedef unsigned char MAP_CELL;
typedef MAP_CELL MAP[MAP_SIZE][MAP_SIZE];


becomes:

typedef unsigned char MAP_CELL;
typedef MAP_CELL MAP[255;][255;];


Obviously not what you want. Get rid of that semicolon in #define and you should be ok. Out of curiosity, what are you doing with these MAP_SIZE ^ 2 bytes?

-Lee

wrldwzrd89
Sep 28, 2008, 01:19 AM
#define has led you astray. You had it right in the original.

That's just a pre-processor directive. Any instance of the first item is replaced with the second.

So:

#define MAP_SIZE 255;

typedef unsigned char MAP_CELL;
typedef MAP_CELL MAP[MAP_SIZE][MAP_SIZE];
becomes:

typedef unsigned char MAP_CELL;
typedef MAP_CELL MAP[255;][255;];
Obviously not what you want. Get rid of that semicolon in #define and you should be ok. Out of curiosity, what are you doing with these MAP_SIZE ^ 2 bytes?

-Lee
Oops! :o

I'm making a map library for a game. You know, those old-school tile-based maps.