多维数组中的数组偏移计算(列与行主要)

时间:2009-04-25 23:13:42

标签: c++ c arrays

我最近阅读的一本教科书讨论了行专业和列主要数组。这本书主要关注1维和2维数组,但没有真正讨论3维数组。我正在寻找一些很好的例子,以帮助巩固我对使用row major& amp ;;来解决多维数组中的元素的理解。列主要数组。

           +--+--+--+  |
          /  /  /  /|  |
         +--+--+--+ +  |        +---+---+---+---+
        /  /  /  /|/|  |       /   /   /   /   /|
       +--+--+--+ + +  |      +---+---+---+---+ +
      /  /  /  /|/|/|  |     /   /   /   /   /|/|
     +--+--+--+ + + +  |    +---+---+---+---+ + +
    /  /  /  /|/|/|/|  |   /   /   /   /   /|/|/|
   +--+--+--+ + + + +  |  +---+---+---+---+ + + +
  /  /  /  /|/|/|/|/   |  |000|001|002|003|/|/|/|
 +--+--+--+ + + + +    |  +---+---+---+---+ + + +
 |00|01|02|/|/|/|/     |  |004|005|006|007|/|/|/|
 +--+--+--+ + + +      |  +---+---+---+---+ + + +
 |03|04|05|/|/|/       |  |008|009|00A|00B|/|/|/
 +--+--+--+ + +        |  +---+---+---+---+ + +
 |06|07|08|/|/         |  |00C|00D|00E|00F|/|/
 +--+--+--+ +          |  +---+---+---+---+ +
 |09|0A|0B|/           |  |010|011|012|013|/
 +--+--+--+            |  +---+---+---+---+
 arr[5][3][4]          |    arr[3][4][5]

注意:原始问题错误地表示为arr [3] [4] [5]。我了解到原始下标代表深度。数据已经过修正,以反映预期的数组表示。

Example hex data
 +---+---+---+---+  +---+---+---+---+  +---+---+---+---+    
 |000|001|002|003|  |100|101|102|103|  |200|201|202|203|  
 +---+---+---+---+  +---+---+---+---+  +---+---+---+---+  
 |004|005|006|007|  |104|105|106|107|  |204|205|206|207|   
 +---+---+---+---+  +---+---+---+---+  +---+---+---+---+   
 |008|009|00A|00B|  |108|109|10A|10B|  |208|209|20A|20B|   
 +---+---+---+---+  +---+---+---+---+  +---+---+---+---+   
 |00C|00D|00E|00F|  |10C|10D|10E|10F|  |20C|20D|20E|20F|
 +---+---+---+---+  +---+---+---+---+  +---+---+---+---+ 
 |010|011|012|013|  |110|111|112|113|  |210|211|212|213|
 +---+---+---+---+  +---+---+---+---+  +---+---+---+---+ 
      slice 0            slice 1            slice 2

 short Arr[3][4][5]; // assume array is filled with hex test data

 arr[1][2][3] = 0x10B use slice 1, row 2, col 3
 arr[2][3][4] = 0x210 use slice 2, row 3, col 4 
                       resolves to row 4, col 0

行专业  {000,001,002,003,004,005,006,007,008,009,00A,00B,00C,00D,00E,00F,010011012013,   100,101,102,103,104,105,106,107,108,109,10A,10B,10C,10D,10E,10F,110111112113,   200,201,202,203,204,205,206,207,208,209,20A,20B,20C,20D,20E,20F,210211212213}

专栏专业  {000,004,008,00C,010,001,005,009,00D,011,002,006,00A,00E,012,003,007,00B,00F,013,   100,104,108,10C,110,101,105,109,10D,111,102,106,10A,10E,112,103,107,10B,10F,113,   200,204,208,20C,210,201,205,209,20D,211,202,206,20A,20E,212,203,207,20B,20F,213}

  Calculation offset for arr[1][2][3] using row major offset?
  Calculation offset for arr[1][2][3] using column major offset?

6 个答案:

答案 0 :(得分:10)

我会看到Row-major order维基百科的文章。有一节描述的尺寸高于2.还有一篇好文章here。该文给出了使用行主要布局的三维数组的以下公式:

Address = Base + ((depthindex*col_size+colindex) * row_size + rowindex) * Element_Size

对于3D阵列:键入A [深度] [col] [行]。基数是数组的起始偏移量。此外,大小变量是每个维度的不同大小。 Element_Size变量表示数组组成的任何类型的大小。

假设你有一个由标准C ++整数组成的行主数组a [4] [6] [5]。要计算1 [3] 2的偏移量,您可以将以下数字插入公式中:

Address = Base + ((1 * 6 + 3) * 5 + 2) * 4

对于具有列主要布局的三维数组,该等式更倾向于:

Address = Base + ((rowindex*col_size+colindex) * depth_size + depthindex) * Element_Size

您使用列主要布局插入上述示例的数字现在是:

Address = Base + ((2 * 6 + 3) * 4 + 1) * 4

答案 1 :(得分:10)

不要通过专注于三维和二维来人为地限制自己。相反,专注于学习解决n维数组的表达式

表达n维寻址将巩固你对这个主题的掌握,并且更容易记住一个公式而不是单独的公式进行二维和三维寻址。


这是我对n维寻址的尝试:

#define LEN 10

int getValue_nDimensions( int * baseAddress, int * indexes, int nDimensions ) {
    int i;
    int offset = 0;
    for( i = 0; i < nDimensions; i++ ) {
        offset += pow(LEN,i) * indexes[nDimensions - (i + 1)];
    }

    return *(baseAddress + offset);
}

int main() {
    int i;
    int * baseAddress;
    int val1;
    int val2;

    // 1 dimensions
    int array1d[LEN];
    int array1d_indexes[] = {2};
    int array1d_nDimensions = 1;
    baseAddress = &array1d[0];
    for(i = 0; i < LEN; i++) { baseAddress[i] = i; }
    val1 = array1d[2];
    val2 = getValue_nDimensions( // Equivalent to: val1 = array1d[2];
        baseAddress,
        &array1d_indexes[0],
        array1d_nDimensions
    );
    printf("SANITY CHECK: %d %d\n",val1,val2);

    // 3 dimensions
    int array3d[LEN][LEN][LEN];
    int array3d_indexes[] = {2,3,4};
    int array3d_nDimensions = 3;
    baseAddress = &array3d[0][0][0];
    for(i = 0; i < LEN*LEN*LEN; i++) { baseAddress[i] = i; }
    val1 = array3d[2][3][4];
    val2 = getValue_nDimensions( // Equivalent to: val1 = array3d[2][3][4];
        baseAddress,
        &array3d_indexes[0],
        array3d_nDimensions
    );
    printf("SANITY CHECK: %d %d\n",val1,val2);

    // 5 dimensions
    int array5d[LEN][LEN][LEN][LEN][LEN];
    int array5d_indexes[] = {2,3,4,5,6};
    int array5d_nDimensions = 5;
    baseAddress = &array5d[0][0][0][0][0];
    for(i = 0; i < LEN*LEN*LEN*LEN*LEN; i++) { baseAddress[i] = i; }
    val1 = array5d[2][3][4][5][6];
    val2 = getValue_nDimensions( // Equivalent to: val1 = array5d[2][3][4][5][6];
        baseAddress,
        &array5d_indexes[0],
        array5d_nDimensions
    );
    printf("SANITY CHECK: %d %d\n",val1,val2);

    return 0;
}

输出:

SANITY CHECK:     2     2
SANITY CHECK:   234   234
SANITY CHECK: 23456 23456

答案 2 :(得分:2)

术语'row major'和'column major'不能很好地转化为第三维。存储的下一个元素来自当前行或当前列的概念分解。这听起来有点滑稽,但这成为“深度主要”与“宽度主要”排序。每个后续元素不再是单个条目,而是一个完整的二维矩阵。

          / X
         / 
        +---+---+---+
       /   /   /   /|  
      +---+---+---+-+-------   
      | 1 | 5 | 9 |/|  Y
      +---+---+---+ +
      | 2 | 6 | A |/|
      +---+---+---+ +
      | 3 | 7 | B |/| 
      +---+---+---+ +
      | 4 | 8 | C |/
      +---+---+---+

因此,存储器在存储器中依次具有1,2,3,4,5,6,7,8,9,10,11,12,13。这是经典的列主要排序。通过将D条目放在标记为X的位置,您没有改变矩阵具有colum主要排序的事实。如果将D条目放在Y所在的位置,您仍然没有改变使用列主要排序的事实。如果您使用深度主要(X)或宽度主要(Y)排序,那么您决定放置下一个块将会影响。如你所知,这些是等价的,但称之为某些东西可能有助于你编写方程式:

[假设基于数组]

您可以通过以下等式访问二维柱主要元素的内存位置:

MatrixOffset = base + (sizeof(entry) * ((4 * ( column - 1 ))   +  (row - 1)))

此地址将使用深度或宽度进行调整,这完全是术语问题。

TotalOffset = MatrixOffset + (sizeof(entry) * ((4 * 3) * (depth - 1))) 

TotalOffset = MatrixOffset + (sizeof(entry) * ((4 * 3) * (width - 1))) 

常数4和3可能是变量COLUMNS和ROWS。

不要问我关于第4维的事情!

答案 3 :(得分:0)

基本上在行主要的3D arrray中E.g Arr [3] [4] [5] 当你想要Arr [0]时,它会像上面那样寻找图像的前切片 Arr [1]第二个切片,依此类推。 所以Arr [0]意味着尺寸为[4] [5]的二维数组,所以每个Arr [x]对应x *(4 * 5) 现在关于Arr [x] [y]可以被认为是一个大小为[1]的一维数组,其位置是上图中顶视图的[x] [y] 所以当我们想要Arr [x] [y] = x *(4 * 5)+ y *(5)时 现在Arr [x] [y] [z]是深度为z的Arr [x] [y]的特定元素 编曲[X] [Y] [Z] = X *(4 * 5)+ Y * 5 + Z 现在根据你想要存储在数组中的数据类型的大小,偏移量可以乘以大小得到地址相对于开始

答案 4 :(得分:0)

当我问这个问题时,我希望找到一些好的三维数组示例。特别是代码示例。由于我没有找到任何可以理解的东西,我决定创建一个小C程序来帮助显示这个概念。它在3x4x5阵列中使用相同的测试数据。它还包括5x5x5阵列的测试数据。它从行主阵列创建列主阵列,以便验证偏移计算。

数组偏移方法是:

  • char * calc_RowMajor(char * Base,int elemSz,int depth_idx,int row_idx,int col_idx)
  • char * calc_ColMajor(char * Base,int elemSz,int depth_idx,int col_idx,int row_idx)

    我在代码中添加了注释,以帮助澄清代码的作用。

    
    //
    // Arrays.cpp : 
    //     Purpose: Display rowMajor & colMajor data and calculations.
    //
    #include "stdafx.h"
    
    #define _show_Arrays 1  // 1=display rowMajor & colMajor arrays
    #define _square_array 0 // 1=use arr[5][5][5], 0=use arr[3][4][5]
    
    #if (_square_array == 1)
        const int depthSz = 5;
        const int rowSz = 5;
        const int colSz = 5;
        /*
        +---+---+---+---+---+
        |x00|x01|x02|x03|x04|
        +---+---+---+---+---+ 
        |x05|x06|x07|x08|x09|   
        +---+---+---+---+---+  
        |x0A|x0B|x0C|x0D|x0E|   
        +---+---+---+---+---+   
        |x0F|x10|x11|x12|x13|
        +---+---+---+---+---+ 
        |x14|x15|x16|x17|x18|
        +---+---+---+---+---+ 
              slice x          
        */
        short row_arr[depthSz][colSz][rowSz] = {
        { /* slice 0 */
          {0x000,0x001,0x002,0x003,0x004},
          {0x005,0x006,0x007,0x008,0x009},
          {0x00A,0x00B,0x00C,0x00D,0x00E},
          {0x00F,0x010,0x011,0x012,0x013},
          {0x014,0x015,0x016,0x017,0x018}},
        { /* slice 1 */
          {0x100,0x101,0x102,0x103,0x104},
          {0x105,0x106,0x107,0x108,0x109},
          {0x10A,0x10B,0x10C,0x10D,0x10E},
          {0x10F,0x110,0x111,0x112,0x113},
          {0x114,0x115,0x116,0x117,0x118}},
        { /* slice 2 */
          {0x200,0x201,0x202,0x203,0x204},
          {0x205,0x206,0x207,0x208,0x209},
          {0x20A,0x20B,0x20C,0x20D,0x20E},
          {0x20F,0x210,0x211,0x212,0x213},
          {0x214,0x215,0x216,0x217,0x218}},
        { /* slice 3 */
          {0x300,0x301,0x302,0x303,0x304},
          {0x305,0x306,0x307,0x308,0x309},
          {0x30A,0x30B,0x30C,0x30D,0x30E},
          {0x30F,0x310,0x311,0x312,0x313},
          {0x314,0x315,0x316,0x317,0x318}},
        { /* slice 4 */
          {0x400,0x401,0x402,0x403,0x404},
          {0x405,0x406,0x407,0x408,0x409},
          {0x40A,0x40B,0x40C,0x40D,0x40E},
          {0x40F,0x410,0x411,0x412,0x413},
          {0x414,0x415,0x416,0x417,0x418}}
        };
    
    #else
      const int depthSz = 3;
        const int rowSz = 4;
        const int colSz = 5;
        /*
        +---+---+---+---+
        |000|001|002|003|  
        +---+---+---+---+  
        |004|005|006|007|   
        +---+---+---+---+   
        |008|009|00A|00B|   
        +---+---+---+---+   
        |00C|00D|00E|00F|
        +---+---+---+---+ 
        |010|011|012|013|
        +---+---+---+---+ 
             slice x
        */
        short row_arr[depthSz][colSz][rowSz] = {
        {  /* slice 0 */
          {0x000,0x001,0x002,0x003},
          {0x004,0x005,0x006,0x007},
          {0x008,0x009,0x00A,0x00B},
          {0x00C,0x00D,0x00E,0x00F},
          {0x010,0x011,0x012,0x013}},
        { /* slice 1 */
          {0x100,0x101,0x102,0x103},
          {0x104,0x105,0x106,0x107},
          {0x108,0x109,0x10A,0x10B},
          {0x10C,0x10D,0x10E,0x10F},
          {0x110,0x111,0x112,0x113}},
        {  /* slice 2 */
          {0x200,0x201,0x202,0x203},
          {0x204,0x205,0x206,0x207},
          {0x208,0x209,0x20A,0x20B},
          {0x20C,0x20D,0x20E,0x20F},
          {0x210,0x211,0x212,0x213}}
        };
    #endif
        short col_arr[depthSz*colSz*rowSz]; //
    
    char *calc_RowMajor(char *Base, int elemSz, int depth_idx, int row_idx, int col_idx)
    {  // row major slice is navigated by rows
      char *address;
      int   lbound = 0; // lower bound (0 for zero-based arrays)
      address = Base        /* use base passed */
         + ((depth_idx-lbound)*(colSz*rowSz*elemSz))    /* select slice */
         + ((row_idx-lbound)*rowSz*elemSz)      /* select row */
         + ((col_idx-lbound)*elemSz);       /* select col */
        return address;
    }
    char *calc_ColMajor(char *Base, int elemSz, int depth_idx, int col_idx, int row_idx)
    {  // col major slice is navigated by columns
      char *address;
      int   lbound = 0; // lower bound (0 for zero-based arrays)
      int   pageSz = colSz*rowSz*elemSz; 
      int   offset;
    
      offset = (col_idx-lbound)*(colSz*elemSz)  /* select column */
             + (row_idx-lbound)*(elemSz);   /* select row */
        if (offset >= pageSz)
        {   // page overflow, rollover
            offset -= (pageSz-elemSz);                          /* ajdust offset back onto page */
        }
        address = Base            /* use base passed */
                + ((depth_idx-lbound)*pageSz)  /* select slice */
                + offset;
        return address;
    }
    
    void disp_slice(char *pStr, short *pArr,int slice,int cols, int rows)
    {
      printf("== %s slice %d == %p\r\n",pStr, slice,pArr+(slice*rows*cols));
      for(int x=0;x<rows;x++)
      {
        for(int y=0;y<cols;y++)
          printf("%03X ",*(pArr+(slice*rows*cols)+(x*cols)+y));
          printf("\r\n");
      }
    }
    
    int _tmain(int argc, _TCHAR* argv[])
    {
      // initialize col based array using row based array data
      { // convert row_arr into col_arr
        short *pSrc = &row_arr[0][0][0];
        short *pDst = &col_arr[0];
        for(int d=0;d<depthSz;d++)
          for(int r=0;r<rowSz;r++)
            for(int c=0;c<colSz;c++)
            {
        *pDst++ = *(pSrc+((d*rowSz*colSz)+(c*rowSz)+r));
            }
      }
    
      printf("Using Array[%d][%d][%d]\r\n",depthSz,rowSz,colSz);
    
    #if (_show_Arrays == 1)
      { for(int x=0;x<depthSz;x++) {disp_slice("rowMajor",&row_arr[0][0][0],x,rowSz,colSz);}}
      { for(int x=0;x<depthSz;x++) {disp_slice("colMajor",&col_arr[0],x,rowSz,colSz);}}
    #endif
    
      int d = 2;    // depth
      int r = 3;    // row
      int c = 4;    // column
    
      for(d=0;d<depthSz;d++)
      { 
        c = r = d;  // simple access test pattern arr[0][0][0],arr[1][1][1],arr[2][2][2],...
        { // retrieve Array element
          printf("    row_arr[%d][%d][%d] = %x\t",d,r,c,row_arr[d][r][c]);
          printf("&row_arr[%d][%d][%d] = %p\r\n",d,r,c,&row_arr[d][r][c]);
        }
        { // retrieve RowMajor element
          short *pRowMajor = (short*)calc_RowMajor((char*)&row_arr[0][0][0],sizeof(short),d,r,c);
          printf("calc_RowMajor(%d,%d,%d) = %x\t\t",d,r,c,*pRowMajor);
          printf("pRowMajor = %p\r\n",pRowMajor);
        }
        {   // retrieve ColMajor element
          short *pColMajor = (short*)calc_ColMajor((char*)&col_arr[0],sizeof(short),d,c,r);
          printf("calc_ColMajor(%d,%d,%d) = %x\t\t",d,r,c,*pColMajor);
          printf("pColMajor = %p\r\n",pColMajor);
        }
     } // for
    
     getchar(); // just to hold the console while looking at the information
      return 0;
    }
    
  • 答案 5 :(得分:0)

    k维数组的公式是

        k-1       n
    i  + ∑  i  *  ∏  c
     0  n=1  n   m=0  m
    

    其中i下标n是n = {0,1,2,... k-1}的维数n的索引 和c下标m是m = {0,1,2,... k-2}的维数m的基数。

    可以在此处找到PNG格式的更好的公式呈现:

    http://modula-2.info/m2r10/pmwiki.php/Spec/LanguageReport#MultiDimensionalArrays