39

我知道对于一维数组 x=a[i]相当于x=*(a+i),但是如何使用指针访问二维数组的元素?

4

6 回答 6

54

总结:如果你有一个定义为的多维数组int [][],则x = y[a][b]等价于x = *((int *)y + a * NUMBER_OF_COLUMNS + b);


无聊的细节:

上面的(int *)演员y值得一些解释,因为它的必要性一开始可能并不直观。要理解为什么它必须在那里,请考虑以下几点:

  1. C/C++ 中的类型化指针算法在通过标量进行加/减/递增/递减时,总是根据类型的大小(以字节为单位)调整类型化指针值(这是一个地址)。

  2. 多维数组声明的基本类型(不是元素类型;变量类型)是比最终维度少一维的数组类型。

其中后者(#2)确实需要一个例子来巩固。在下文中,变量ar1ar2是等效的声明。

int ar1[5][5]; // an array of 5 rows of 5 ints.

typedef int Int5Array[5];  // type is an array of 5 ints
Int5Array ar2[5];          // an array of 5 Int5Arrays.

现在是指针算术部分。就像一个类型化的结构指针可以按结构的大小(以字节为单位)前进一样,数组的整个维度也可以跳过。如果您想到我在上面声明的 ar2 的多维数组,这将更容易理解:

int (*arptr)[5] = ar1; // first row, address of ar1[0][0].
++arptr;               // second row, address of ar[1][0].

所有这一切都随着一个裸指针而消失:

int *ptr = ar1; // first row, address of ar1[0][0].
++ptr;          // first row, address of ar1[0][1].

因此,在对二维数组进行指针运算时,以下方法在获取[2][2]多维数组的元素时不起作用:

#define NUMBER_OF_COLUMNS   5
int y[5][NUMBER_OF_COLUMNS];
int x = *(y + 2 * NUMBER_OF_COLUMNS + 2); // WRONG

当你记得那y是一个数组数组(声明式的)时,原因就很明显了。(2*5 + 2)添加定标器的指针算法y将添加 12,从而计算和地址等价于&(y[12]),这显然是不对的,实际上,要么在编译时抛出一个胖警告,要么完全编译失败。(int*)y表达式的强制转换和结果类型基于裸指针 int可以避免这种情况:

#define NUMBER_OF_COLUMNS   5
int y[5][NUMBER_OF_COLUMNS];
int x = *((int *)y + 2 * NUMBER_OF_COLUMNS + 2); // Right!
于 2012-11-25T18:54:43.647 回答
25

桌子

在 C 中,二维数组是一系列连续的线(不像在 Pascal 中)。
当我们创建一个 4 行 5 列的整数表时: 一个 5*4 整数表。

到达元素

我们可以通过以下方式达到元素:

int element = table[row-1][column-1];

但是我们也可以使用以下代码来做到这一点:

int element = *(*(table+row-1)+column-1);

在这些示例中rowcolumn从 1 开始计数,这就是 -1 的原因。
在下面的代码中,您可以测试这两种技术是否正确。在这种情况下,我们从 0 开始计算行和列。

例子

#include <stdio.h>
#include <stdlib.h>
#define HEIGHT 4
#define WIDTH 5

int main()
{
    int table[HEIGHT][WIDTH] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20};
    int row = 2;
    int column = 2;
    int a = *(*(table+row)+column);
    printf("%d\n",a);//13
    printf("%d\n",table[row][column]);//13
    return 0;
}

解释

这是一个双指针算术,因此table指向第一行并*table指向第一个元素,如果您取消引用它,**table则将返回第一个元素的值。*table在下面的示例中,您可以看到它table指向相同的内存地址。

printf("%d\n",table);//2293476
printf("%d\n",*table);//2293476
printf("%d\n",**table);//1

在内存中,表格的所有行都相互跟随。因为table如果我们在表中需要的元素所在的位置添加行号,则指向第一行,我们将得到一个指向该行的指针。在这种情况下*(table+row),将包含给定行的第一个元素的地址。现在我们只需要像 一样添加列号*(table+row)+column,我们就可以得到给定行和列中元素的地址。如果我们取消引用这个,我们会得到这个元素的确切值。
因此,如果我们从零开始计算行和列,我们可以从表中获取元素,如下所示:

int element = *(*(table+row)+column);

在记忆中

内存中的表。

于 2012-11-25T19:29:23.100 回答
20

二维数组被视为一维数组的数组。也就是说,二维数组中的每一行都是一维数组。因此给定一个二维数组A

int A[m][n].

一般来说,

A[i][j] = *(A[i]+j) 

A[i] = *(A+i)

所以,

A[i][j] = *(A[i]+j) = * ( *(A+i)+j).
于 2012-11-25T19:01:20.923 回答
6

前面的答案已经解释的很好了,我只是根据我的理解列出指针表达式,并将它们与arr[i][j]格式进行比较。

二维数组的指针表达式:
    数组名本身是指向第一个子数组的指针,

    arr:
        将是指向第一个子数组的指针,而不是第一个子数组的第一个元素
        数组,根据数组和指针的关系,也表示
        数组本身,

    arr+1:
        将是指向第二个子数组的指针,而不是第一个子数组的第二个元素
        大批,

    *(arr+1) :
        将指向第二个子数组的第一个元素,
        根据数组和指针的关系,它也代表第二个
        子数组,同arr[1]*(arr+1)+2:
        将指向第二个子数组的第三个元素,

    *(*(arr+1)+2):
        将获得第二个子数组的第三个元素的值,
        与arr[1][2]相同,

与二维数组类似,多维数组也有类似的表达。

于 2014-10-16T08:19:52.390 回答
2

一种使用指针访问的实用方法。

typedef struct
{
    int  Array[13][2];
} t2DArray;

t2DArray TwoDArray =
{
   { {12,5},{4,8},{3,6},{7,9},{3,2},{3,3},{3,4},{3,5},{3,6},{3,7},{4,0},{5,0},{5,1} }
};

t2DArray *GetArray;

int main()
{
    GetArray = &TwoDArray;
    printf("\n %d\n %d\n %d\n %d\n %d\n %d\n",
    GetArray->Array[0][0], 
    GetArray->Array[0][1], 
    GetArray->Array[1][0], 
    GetArray->Array[1][1], 
    GetArray->Array[2][0], 
    GetArray->Array[2][1]);

    getchar();
    return 0;
}

出去

12 5 4 8 3 6

于 2018-07-15T21:38:18.807 回答
0
#include <iostream>
using namespace std;

int main()
{
   //FOR 1-D ARRAY THROUGH ARRAY
   int brr[5]= {1,2,3,4,5};

   for(int i=0; i<5; i++)
   {
      cout<<"address ["<<i<<"] = "  <<&brr[i]<<" and value = "<<brr[i]<<endl;        
   }

   //FOR 1-D ARRAY THROUGH POINTER
   cout<<endl;  //  endl TO MAKE OUT PUT LOOK CLEAR AND COOL :)
   int (*q)=brr;

   for(int i=0; i<5; i++)
   {
      cout<<"address ["<<i<<"] = "  <<&brr[i]<<" and value = "<<*(q+i)<<endl; //(p[i][j])
   }

   cout<<endl;

   //FOR 2-D ARRAY THROUGH ARRAY        
   int arr[2][3] = {1,2,3,4,5,6};

   for(int i=0; i<2; i++)
   {
      for(int j=0; j<3; j++)
      {
         cout<<"address ["<<i<<"]["<<j<<"] = "  <<&arr[i][j]<<" and value = "<<arr[i][j]<<endl;
      }
   }

   //FOR 2-D ARRAY THROUGH POINTER        
   int (*p)[3]=arr; //  j value we give
   cout<<endl;

   for(int i=0; i<2; i++)
   {
      for(int j=0; j<3; j++)
      {
         cout<<"address ["<<i<<"]["<<j<<"] = "  <<(*(p+i)+j)<<" and value = "<<(*(*(p+i)+j))<<endl; //(p[i][j])
      }
   }
   return 0;
}

==============OUT PUT======================

//FOR 1-D ARRAY THROUGH ARRAY

address [0] = 0x28fed4 and value = 1
address [1] = 0x28fed8 and value = 2
address [2] = 0x28fedc and value = 3
address [3] = 0x28fee0 and value = 4
address [4] = 0x28fee4 and value = 5

//FOR 1-D ARRAY THROUGH POINTER

address [0] = 0x28fed4 and value = 1
address [1] = 0x28fed8 and value = 2
address [2] = 0x28fedc and value = 3
address [3] = 0x28fee0 and value = 4
address [4] = 0x28fee4 and value = 5

//FOR 2-D ARRAY THROUGH ARRAY

address [0][0] = 0x28fee8 and value = 1
address [0][1] = 0x28feec and value = 2
address [0][2] = 0x28fef0 and value = 3
address [1][0] = 0x28fef4 and value = 4
address [1][1] = 0x28fef8 and value = 5
address [1][2] = 0x28fefc and value = 6

//FOR 2-D ARRAY THROUGH POINTER

address [0][0] = 0x28fee8 and value = 1
address [0][1] = 0x28feec and value = 2
address [0][2] = 0x28fef0 and value = 3
address [1][0] = 0x28fef4 and value = 4
address [1][1] = 0x28fef8 and value = 5
address [1][2] = 0x28fefc and value = 6
于 2017-09-20T16:56:53.187 回答