3

I have some data stored in a one dimensional array of size say 'M'. Now I need to treat it as a two dimensional array with dimension NxP, where the product of N and P is equal to M. I know the values of N and P only at runtime. How can I implement such a function in C?

int array[M]; /* one dimensional array where some data is stored*/
int** newArray; /* the dimension of newArray should be NxP such that we can access the data in 'array' as a two-dimensional array*/
WhozCraig
  • 65,258
  • 11
  • 75
  • 141
user4661268
  • 49
  • 1
  • 7

5 Answers5

5

Just cast it to the appropriate array pointer type:

int (*newArray)[N] = (int (*)[N])array;

After that, you can access the array with:

for(int y = 0; y < P; y++) {
    for(int x = 0; x < N; x++) {
        array[y][x] = 42;
    }
}

This is equivalent to the following indexing:

for(int y = 0; y < P; y++) {
    for(int x = 0; x < N; x++) {
        newArray[y*N + x] = 42;
    }
}

This works even if N is only known at run time since C99. Note that you do not need to set up an index array that way, as you would have to do if you used an int**.

cmaster - reinstate monica
  • 38,891
  • 9
  • 62
  • 106
  • 1
    Yes, this is the correct way to go, pointers to pointers are just the wrong tool from the start. – Jens Gustedt Aug 31 '15 at 18:43
  • How would that be for a 3D array (e.g. `array[X][Y][Z]`)? I tried `int (*newArray)[X][Y] = (int (*)[X][Y]) array`, it doesn't work :-/ – ijverig Nov 13 '17 at 15:06
  • @ijverig That's wrong: It is the out-most dimension that's omitted from the pointer. Thus, for the dimension order `array[X][Y][Z]`, you need to drop the `[X]`: `int (*newArray)[Y][Z] = (int (*)[Y][Z])array;` – cmaster - reinstate monica Nov 13 '17 at 15:28
  • @ijverig Note that we conventionally use the opposite dimension order in C: `array[Z][Y][X]`, and by consequence `int (*newArray)[Y][X] = (int (*)[Y][X])array;` That way, we have all the `X` values of a single line at `array[z][y]` stored together. – cmaster - reinstate monica Nov 13 '17 at 15:31
2

You don't need to define a new array. You can use the existing one.

Assuming you know N and P, and N is the number of rows, you can access item (i,j) as:

array[i*N + j]
dbush
  • 205,898
  • 23
  • 218
  • 273
1

You could do it like this:

int ** newArray = malloc(sizeof(int*) * N);
for (i = 0; i < N; ++i) {
  newArray[i] = array[i * J];
}

This will make an array that "looks" just like a dynamically allocated 2D array of N rows and J columns, but in fact points to the rows of the 1D array.

That way if you have functions to operate on 2D arrays already, you don't need to rewrite them to use the 1D syntax described in other answer.

Chris Beck
  • 15,614
  • 4
  • 51
  • 87
  • Thanks for the reply. I want to avoid the additional memory allocations! – user4661268 Aug 31 '15 at 18:41
  • Then declare it on the stack like `int * newArray[N];` instead of mallocing. I assumed you wanted to malloc since you said `int **` in your answer. Note that if you return it though you'll have to malloc it. – Chris Beck Aug 31 '15 at 18:43
1

The runtime makes this a little harder, but :-

newArray = malloc( sizeof( int*) * N ); /* create an array of pointers.
{ 
     size_t i;
     for( i = 0; i < N; i++ ) {
         newArray[i] = &array[ i* P];
     }
}

/* Now newArray[i][j] is usable */

mksteve
  • 12,614
  • 3
  • 28
  • 50
0

You can just cast the 1d array as the 2d array you want. It's just a block of memory.

int _tmain(int argc, _TCHAR* argv[])
{
    int oneDArray[12] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };
    int(*twoDArray)[3] = (int(*)[3])&oneDArray[0]; // This is the magic line!

    for (int i = 0; i < 4; i++)
    {
        for (int j = 0; j < 3; j++)
        {
            printf("i: %d j: %d value: %d\r\n", i, j, twoDArray[i][j]);
        }
    }

    _getch();

    return 0;
}

Also see question Convert Array Question

There's some inherent unsafeness in doing this, but your question states that NxP=M, so if that's true it will work. People will frown at it though.

Community
  • 1
  • 1
AngularRat
  • 602
  • 3
  • 8
  • my vote limit is exceeded for the day but if it wasn't I would plus one this :) thanks for this trick – Chris Beck Aug 31 '15 at 18:58
  • Well, make sure you understand what's going on. A pointer in c is just memory, so you can treat it like it contains anything. BUT if you get your indexes wrong, c doesn't care if you read past the end of what's actually allocated, which is what leads to the biggest complaint people have about c/c++, which is buffer overruns. – AngularRat Aug 31 '15 at 19:00
  • i mean the other thing is C has lots of pitfalls regarding alignment of structures, sometimes its not intuitive where stuff gets placed, see here for instance http://stackoverflow.com/questions/23367937/memory-alignment-of-arrays but I guess with arrays its always going to be continguous, so in your example casting to `*int[3]` is okay. the point is `int[12]` gets aligned to `int` boundary and otherwise the offsets are always a linear progression (if it didn't work that way it would be a lot slower to dereference arrays in general) I think your trick would be okay even if with `struct foo[12]` – Chris Beck Aug 31 '15 at 19:03
  • If your data type stays the same in both array definitions, you don't really have to worry about the way it's stored because the compiler knows an int is x bytes, so index 0 is byte 0, index 1 is byte (1 x sizeof(int) ), etc. However, if you did something crazy like saving the array to disk and reloading it on a machine with a different architecture that defined ints differently, you'd have an issue. (Pretty sure ints can be different sizes on different architectures.) – AngularRat Aug 31 '15 at 19:07