118

I want to represent a 2D array with a 1D array. A function will pass the two indicies (x,y) and the value to store. These two indicies would represent a single element of a 1D array, and set it accordingly. I know the 1D array needs to have the size of arrayWidth × arrayHeight, but I don't know how to set each element.

For example, how do I distinguish (2,4,3) from (4,2,3)? I tried setting the array as the x*y, but 2*4 and 4*2 would result in the same spot in the array and I need them to be different.

Palec
  • 12,743
  • 8
  • 69
  • 138
Blackbinary
  • 3,936
  • 18
  • 49
  • 62

7 Answers7

213

You need to decide whether the array elements will be stored in row order or column order and then be consistent about it. http://en.wikipedia.org/wiki/Row-major_order

The C language uses row order for Multidimensional arrays

To simulate this with a single dimensional array, you multiply the row index by the width, and add the column index thus:

 int array[width * height];

 int SetElement(int row, int col, int value)
 {
    array[width * row + col] = value;  
 }
Yves M.
  • 29,855
  • 23
  • 108
  • 144
John Knoeller
  • 33,512
  • 4
  • 61
  • 92
  • 7
    I think this answer is more clearer, especially for beginners it's better not writing functions in one line...!! It's bad practice anyway.. :) – Lipis Jan 27 '10 at 23:38
  • 3
    This answer is also useful for when you have a compiler (e.g. embedded systems) that doesn't have proper multidimensional array support – Alex Marshall Dec 06 '13 at 21:50
  • 2
    It is amazing how many people can answer the same question correctly, but only ONE of them says it in a way that is easy to understand. This is as simple of an answer as it gets. However, John is the only one to actually provide a good answer for it. All the rest is trash that can only be easily understood by those who already know the answer. Thanks John, for actually speaking in english instead of alien. Just goes to show how bad some people are at teaching, and how good teachers like John Knoeller know how to simplify and communicate much more effectively than everyone else. – user2948630 Feb 15 '14 at 02:03
  • 8
    It would be good to show how to invert this mapping: if the index of the 1D array is `alpha`, and the 2D array has dimension `N` in both directions with indices `x, y`, then according to @JohnKnoeller, `alpha=x+N*y`. The way to invert this would be setting `x=alpha%N` and `y= (alpha-alpha%N)/N`. – Tim Aug 08 '16 at 09:22
33

The typical formula for recalculation of 2D array indices into 1D array index is

index = indexX * arrayWidth + indexY;

Alternatively you can use

index = indexY * arrayHeight + indexX;

(assuming that arrayWidth is measured along X axis, and arrayHeight along Y axis)

Of course, one can come up with many different formulae that provide alternative unique mappings, but normally there's no need to.

In C/C++ languages built-in multidimensional arrays are stored in memory so that the last index changes the fastest, meaning that for an array declared as

int xy[10][10];

element xy[5][3] is immediately followed by xy[5][4] in memory. You might want to follow that convention as well, choosing one of the above two formulae depending on which index (X or Y) you consider to be the "last" of the two.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
25

Example : we want to represent an 2D array of SIZE_X and SIZE_Y size. That means that we will have MAXY consecutive rows of MAXX size. Hence the set function is

void set_array( int x, int y, int val ) { array[ x * SIZE_Y + y ] = val; }

The get would be:

int get_array( int x, int y ) { return array[ x * SIZE_Y + y ]; }
Kornel Kisielewicz
  • 55,802
  • 15
  • 111
  • 149
  • 1
    Your `MAXX` and `MAXY` values are confusingly named, because the maximum values of `x` and `y` are `MAXX - 1` and `MAXY - 1` respectively. Perhaps `SIZE_X` and `SIZE_Y` might be better? – caf Jan 27 '10 at 23:29
  • 3
    [y * maxx + x] is column order, not row order. This is the way matlab works, but isn't the way arrays normally work in C. – John Knoeller Jan 27 '10 at 23:33
  • @everyone: unless you keep data touched but purely these two get/set functions, and they use the same formula, you can do it like THIS or like THAT. (Guaranteed!) – imacake Nov 04 '11 at 18:37
  • A macro might be more appropriate here so you're not stacking unnecessary function calls on what is supposed to be a low-level data access (especially since 1d-indexing in pseudo-2d arrays is sometimes an optimization technique. – krs013 Sep 19 '14 at 00:00
  • Assuming that the code is a class member, this code will get inlined. Otherwise explicit inline is *MUCH* better than a macro. – Kornel Kisielewicz Sep 24 '14 at 17:11
  • @KornelKisielewicz - I know this is an old question, but did you mean: "MAXY consecutive rows of MAXX size?" – Rohan Oct 27 '15 at 09:07
  • @JohnKnoeller That statement conflicts with [this answer](https://stackoverflow.com/a/2151141/6243352), which shows `y` being used first, to access the row. – ggorlen Aug 24 '23 at 21:06
10

As other have said C maps in row order

   #include <stdio.h>

   int main(int argc, char **argv) {
   int i, j, k;
   int arr[5][3];
   int *arr2 = (int*)arr;

       for (k=0; k<15; k++) {
          arr2[k] = k;
          printf("arr[%d] = %2d\n", k, arr2[k]);
       }

       for (i=0; i<5; i++) {
         for (j=0; j< 3; j++) {
            printf("arr2[%d][%d] = %2d\n", i, j ,arr[i][j]);
         }
       } 
    } 

Output:

arr[0] =  0
arr[1] =  1
arr[2] =  2
arr[3] =  3
arr[4] =  4
arr[5] =  5
arr[6] =  6
arr[7] =  7
arr[8] =  8
arr[9] =  9
arr[10] = 10
arr[11] = 11
arr[12] = 12
arr[13] = 13
arr[14] = 14
arr2[0][0] =  0
arr2[0][1] =  1
arr2[0][2] =  2
arr2[1][0] =  3
arr2[1][1] =  4
arr2[1][2] =  5
arr2[2][0] =  6
arr2[2][1] =  7
arr2[2][2] =  8
arr2[3][0] =  9
arr2[3][1] = 10
arr2[3][2] = 11
arr2[4][0] = 12
arr2[4][1] = 13
arr2[4][2] = 14
Sammy
  • 467
  • 3
  • 13
4

using row major example:

A(i,j) = a[i + j*ld]; // where ld is the leading dimension
                      // (commonly same as array dimension in i)

// matrix like notation using preprocessor hack, allows to hide indexing
#define A(i,j) A[(i) + (j)*ld]

double *A = ...;
size_t ld = ...;
A(i,j) = ...;
... = A(j,i);
Anycorn
  • 50,217
  • 42
  • 167
  • 261
1

It's important to store the data in a way that it can be retrieved in the languages used. C-language stores in row-major order (all of first row comes first, then all of second row,...) with every index running from 0 to it's dimension-1. So the order of array x[2][3] is x[0][0], x[0][1], x[0][2], x[1][0], x[1][1], x[1][2]. So in C language, x[i][j] is stored the same place as a 1-dimensional array entry x1dim[ i*3 +j]. If the data is stored that way, it is easy to retrieve in C language.

Fortran and MATLAB are different. They store in column-major order (all of first column comes first, then all of second row,...) and every index runs from 1 to it's dimension. So the index order is the reverse of C and all the indices are 1 greater. If you store the data in the C language order, FORTRAN can find X_C_language[i][j] using X_FORTRAN(j+1, i+1). For instance, X_C_language[1][2] is equal to X_FORTRAN(3,2). In 1-dimensional arrays, that data value is at X1dim_C_language[2*Cdim2 + 3], which is the same position as X1dim_FORTRAN(2*Fdim1 + 3 + 1). Remember that Cdim2 = Fdim1 because the order of indices is reversed.

MATLAB is the same as FORTRAN. Ada is the same as C except the indices normally start at 1. Any language will have the indices in one of those C or FORTRAN orders and the indices will start at 0 or 1 and can be adjusted accordingly to get at the stored data.

Sorry if this explanation is confusing, but I think it is accurate and important for a programmer to know.

Mark
  • 11
  • 2
-2

You should be able to access the 2d array with a simple pointer in place. The array[x][y] will be arranged in the pointer as p[0x * width + 0y][0x * width + 1y]...[0x * width + n-1y][1x * width + 0y] etc.

Arthur Kalliokoski
  • 1,627
  • 12
  • 12