Using a 1D array like a 2D (or 3D) array

So I’ve been working on a 3D graphics project for school, and I’ve come across an inconvenience: while g++, the linux compiler, will happily let you declare a new multi-dimensional array with a variable as the size, Visual Studio (the MicroSoft compiler) complains that such a daunting task is too difficult — “I expect a FIXED, STATIC size for these arrays!” it says.

For example:

void blah(int width, int height) {
    unsigned char Array[height][width];
}

Seems logical, right? Create a new array, with whatever width and height were passed to the function. And, as I said, g++ will happily let you do this (well, maybe not this particular example, but you get the idea), but VS complains.

As such, and since I have to have a Windows binary to submit to the professor, I’ve ventured into the world of malloc. Sounds like a villain’s name, doesn’t it? For those who don’t know, malloc is (one of?) C’s memory allocation function(s) — give it the amount of memory you need, it’ll reserve that much memory and return the base address (in the form of a “void *”. pfft.). Of course, it’s not that simple, but that’s the basic idea.

To get our two-dimensional array with malloc, you need to get fancy with the looping. For example:

void blah(int width, int height) {
    unsigned char** Array = (unsigned char**)malloc(height*sizeof(unsigned char*));
    for (int i=0; i<height ; i++) {
        Array[i] = (unsigned char*)malloc(width*sizeof(unsigned char));
    }
}

What a freakin’ mess! Instead of reading “create a height-by-width array”, this reads more like “eat up as much memory as you can”. Yes, you have to semi-manually create each level of the “dynamic” array, all so you can conveniently use “Array[n][m]” later in your program. Want a three-dimensional array (used to hold RGB images — Array[height][width][3])?

void blah(int width, int height) {
    unsigned char*** Array = (unsigned char***)malloc(height*sizeof(unsigned char**));
    for (int i=0; i<height ; i++) {
        Array[i] = (unsigned char**)malloc(width*sizeof(unsigned char*));
        for (int j=0; j<width; j++) {
            Array[i][j] = (unsigned char*)malloc(3*sizeof(unsigned char));
        }
    }
}

And so on for each dimension you wish to add. Truly hideous. So many levels of pointers to keep track of, so many stinking loops! There is such a better way :D

Instead of using unsightly code to allow the convenience of multiple subscripts, you could just as well create a width*height(*3), single-dimensional array, and use some simple math to get the proper offset. Here are examples of up to three dimensions (I’ve actually used the three-dimension one in my program to load an image file, works perfectly):

// One dimension -- easy enough:
unsigned char* OneDim = (unsigned char*)malloc(width*sizeof(unsigned char));
for (int i=0; i<width ; i++) {
    OneDim[i];
}

// Two dimensions -- a little trickier:
unsigned char* TwoDim = (unsigned char*)malloc(width*height*sizeof(unsigned char));
for (int i=0; i<height; i++) {
    for (int j=0; j<width; j++) {
        // TwoDim[i][j] =
        TwoDim[i*width+j];
    }
}

// Three dimensions -- w00t, crazy!
unsigned char* ThreeDim = (unsigned char*)malloc(width*height*3*sizeof(unsigned char));
for (int i=0; i<height; i++) {
    for (int j=0; j<width; j++) {
        for (int k=0; k&lt;3; k++) {
            // ThreeDim[i][j][k] =
            ThreeDim[(i*width+j)*3+k];
        }
    }
}

If I’m not mistaken, the recursive pattern for the subscript is “(other-loops)*max+counter”, starting from the innermost loop. So three dimensions goes like this:

(other-loops)*3+k
((other-loops)*width+j)*3+k
(((other-loops)*height+i)*width+j)*3+k

no more loops, use "0":

(((0)*height+i)*width+j)*3+k
((i)*width+j)*3+k
(i*width+j)*3+k

So, for a fourth loop “m” going from 0 to, say, 40, we would probably have:

((i*width+j)*3+k)*40+m

And so on, but who uses 4D arrays, anyways?

Disclaimers:
“for (int i=0; …” syntax is, as far as I know, a C++ feature; in true C, you would have to declare “i” beforehand, then use it in the loop.
When creating objects with malloc, be sure to free the memory when you’re done with it; I refuse to be held responsible for memory leaks in your program.
C library “stdlib.h” must be included to use these functions.
Yes, you have to typecast malloc’s return value to whatever your data type is, unless your variable itself is a “void *”, but why would you need a pointer to nothing?
For each level of the array in the first malloc example, you need as many “*” as you have levels left to create — three for the first level, two for the next, one for the last, etc.
Strictly speaking, “sizeof(unsigned char)” translates to “1″, so it’s not necessary here; however, it is required to get the right size for other data types, in which case the hideous factor is no more reduced.
I have not tested, nor plan on testing, the four-dimensional subscript example given above. If it doesn’t work and you figure out how to make it work, feel free to post your solution :D

Leave a Comment

You need to enable javascript in order to use Simple CAPTCHA.
Security Code: