it-roy-ru.com

Как использовать выражения указателя для доступа к элементам двумерного массива в C?

Я знаю, что для одномерных массивов x=a[i] эквивалентен x=*(a+i), но как я могу получить доступ к элементам двумерных массивов, используя указатели?

35
Tudor Ciotlos

Сводка: Если у вас есть многомерный массив, определенный как int [][], то x = y[a][b] эквивалентен x = *((int *)y + a * NUMBER_OF_COLUMNS + b);


Скучные детали:

Приведение (int *) к y выше заслуживает некоторого объяснения, так как его необходимость на первый взгляд может быть не интуитивной. Чтобы понять, почему это должно быть там, рассмотрите следующее:

  1. Арифметика типизированного указателя в C/C++ всегда корректирует значение типизированного указателя (который является адресом) на размер байтов type in при добавлении/вычитании/увеличении/уменьшении с помощью скаляра.

  2. Фундаментальный type объявления многомерного массива (не тип элемента; тип variable) - это тип массива с одним измерением, меньшим, чем конечное измерение.

Последний (# 2) из ​​них действительно нуждается в примере, чтобы укрепиться. В дальнейшем переменные ar1 и ar2 являются эквивалентными объявлениями.

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 row, таким образом вычисляя и адрес, эквивалентный &(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!
52
antonijn

Стол

В C 2D массивы представляют собой непрерывные серии линий (не как в Pascal).
Когда мы создаем таблицу целых чисел с 4 строками и 5 столбцами: A 5*4 integer table.

Достигая элементов

Мы можем добраться до Elemnts с:

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

Но мы также можем сделать это с помощью следующего кода:

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

В этих примерах row и column отсчитываются от 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);

В памяти

The table in the memory.

23
totymedli

2D-массив рассматривается как массив 1D-массивов. То есть каждая строка в двумерном массиве является одномерным массивом. Поэтому, учитывая двумерный массив A,

int A[m][n].

В общем,

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

также 

A[i] = *(A+i)

так, 

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

Предыдущие ответы уже объяснили очень хорошо, Я просто перечислил бы выражения указателя согласно моему пониманию, и сравнил бы их с обр [я] [J] формат.

 Указатель выражения 2-Д  Массив: 
 само имя массива является указателем на первый вложенный массив, 

 прибытие: 
 будет указателем на первый вложенный массив, а не на первый элемент первого подчиненного 
 массив, в соответствии с отношением массива и указателя, он также представляет 
 сам массив, 

 обр + 1: 
 будет указателем на второй вложенный массив, а не на второй элемент первого подчиненного 
 массив, 

 * (Обр + 1): 
 будет указателем на первый элемент второго подмассива, 
 в соответствии с отношением массива и указателя, он также представляет второй 
 вложенный массив, такой же как обр [1], 

 * (Обр + 1) +2: 
 будет указателем на третий элемент второго подмассива, 

 * (* (Обр + 1) + 2): 
 получит значение третьего элемента второго подмассива, 
 такой же как обр [1] [2], 

Аналогично 2-D массиву, кратно-Д массив имеет похожее выражение.

6
Eric Wang

Практичный способ доступа с помощью указателя.

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

2
CTastan
#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
0
ankit gupta