如何使用指针表达式访问 C 中的二维数组的元素?

Posted

技术标签:

【中文标题】如何使用指针表达式访问 C 中的二维数组的元素?【英文标题】:How to use pointer expressions to access elements of a two-dimensional array in C? 【发布时间】:2012-11-13 07:17:32 【问题描述】:

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

【问题讨论】:

你了解多维数组在内存中是如何布局的吗?你能访问a[0][0]吗? a[0][1]? a[0][ARY_N_Y]? a[1][0]? C 实际上没有二维数组。 @RussellBorogove 这种说法依赖于 “数组数组与二维数组不同” 的说法,这总是归结为一些吊坠声称一块内存是一个二维数组意味着什么。在任何情况下,int a[5][5]; 看起来和行为都像一个二维数组。至少在它衰减为指针之前。 是“学究”,不是“吊坠”。 e: touché 上应该有一个重音 aigu。在 Mac 上,您可以使用 Alt-E(又名 option-E)E 获得该字符。 【参考方案1】:

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


无聊的细节:

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

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

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

后者(#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!

【讨论】:

@WhozCraig 那么它将是 ROW_SIZE,不是吗?还是改了。 我尝试通过打印像 printf("%d",*(a + 2 * 3 + 3)); 这样的二维数组的一个元素来测试它。它应该打印行宽为 3 的矩阵的元素 a[2][3]。在我的情况下,元素 a[2][3] 是 6,但打印的是 0。我做错了吗? @TudorCiotlos “我做错了吗?”我怎么知道?您没有提供来源(顺便说一句,请在另一个问题中提供)。 @TudorCiotlos 可能不是,这里的指针算法不对。正确的算法在the other answer 中,忽略最后一句,二维数组与int** 非常不同。 这是不正确的。 y+1 已经跳过了整个第一行。【参考方案2】:

桌子

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

到达元素

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

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 将返回第一个元素的值。在下面的例子中你可以看到*tabletable指向同一个内存地址。

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);

在记忆中

【讨论】:

我喜欢你的图形,但你必须提到 table 将被视为 char 指针,你仍然必须添加元素的 sizeof。如果它仍然是 2d 数组的类型,则通过添加 1 来跳过整行 int table[rows][cols]; 不正确。在(table + integer_expression) 中,table 被转换为int (*)[cols]【参考方案3】:

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

int A[m][n].

一般来说,

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

还有

A[i] = *(A+i)

所以,

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

【讨论】:

太接近投票了,但最后一句话太不正确,无法按原样投票。不要将二维数组 (int arr[rows][cols];) 与双指针 (int **pp;) 混淆。如果您删除它,我将非常乐意投票。【参考方案4】:

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

2-D 数组的指针表达式: 数组名本身是指向第一个子数组的指针, arr: 将是指向第一个子数组的指针,而不是第一个子数组的第一个元素 数组,根据数组和指针的关系,也表示 数组本身, arr+1: 将是指向第二个子数组的指针,而不是第一个子数组的第二个元素 大批, *(arr+1): 将指向第二个子数组的第一个元素, 根据数组和指针的关系,它也代表第二个 子数组,同arr[1]*(arr+1)+2: 将指向第二个子数组的第三个元素, *(*(arr+1)+2): 将获得第二个子数组的第三个元素的值, 与 arr[1][2] 相同,

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

【讨论】:

【参考方案5】:

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

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

【讨论】:

【参考方案6】:
#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

【讨论】:

请编辑您的答案并为您的解决方案添加一些解释。

以上是关于如何使用指针表达式访问 C 中的二维数组的元素?的主要内容,如果未能解决你的问题,请参考以下文章

C语言如何定义指针指向字符型二维数组

c语言如何表示二维数组里面有#

C语言数组指针&数组名和数组名数组是首元素地址(两特殊情况)数组指针访问二维数组数组指针的使用。

我可以使用 C++ 中的指针访问二维数组的元素吗?

如何使用指针访问这个二维数组的元素?

C 数组使用时注意点