web-dev-qa-db-ja.com

ポインター式を使用してCの2次元配列の要素にアクセスする方法は?

1次元配列の場合_x=a[i]_はx=*(a+i)と同等であることを知っていますが、ポインターを使用して2次元配列の要素にアクセスするにはどうすればよいですか?

37
Tudor Ciotlos

概要:多次元配列がint [][]として定義されている場合、x = y[a][b]x = *((int *)y + a * NUMBER_OF_COLUMNS + b);と同等です


退屈な詳細:

上記のy(int *)キャストは、その必要性が最初は直感的ではない可能性があるため、いくつかの説明に値します。なぜ存在する必要があるのか​​を理解するには、次のことを考慮してください。

  1. C/C++の型付きポインター演算は、スカラーによる加算/減算/インクリメント/デクリメント時に、型付きポインター値(アドレス)をtypeバイト単位のサイズで常に調整します。

  2. 多次元配列宣言の基本type(要素タイプではなく、variabletype)は、最終次元よりも1少ない次元の配列型です。

これらの後者(#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][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に追加するポインター演算は、12rowsを追加し、それにより&(y[12])、これは明らかに正しくありません。実際、コンパイル時に太った警告がスローされるか、まったくコンパイルに失敗します。これは、(int*)yのキャストと、結果の型の整数型へのポインターに基づいているため、回避されます。

#define NUMBER_OF_COLUMNS   5
int y[5][NUMBER_OF_COLUMNS];
int x = *((int *)y + 2 * NUMBER_OF_COLUMNS + 2); // Right!
53
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);
_

これらの例では、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);
_

記憶に

The table in the memory.

24
totymedli

2D配列は、1D配列の配列と見なされます。つまり、2D配列の各行は1D配列です。したがって、2D配列Aが与えられた場合、

int A[m][n].

一般に、

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

また

A[i] = *(A+i)

そう、

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

前の回答はすでに非常によく説明されていたので、私は自分の理解に従ってポインタ式をリストし、それらと比較します arr [i] [j] フォーマット。

のポインター表現 2-D array:
配列名自体は、最初のサブ配列へのポインターです。
 
 到着:
は、最初のサブ
配列の最初の要素ではなく、最初のサブ配列へのポインターになります。配列とポインターの関係により、
は配列自体も表します。 ____。] 
 arr + 1:
は、最初のサブ
配列の2番目の要素ではなく、2番目のサブ配列へのポインターになります。
 
 *(arr + 1):
は、2番目のサブ配列の最初の要素へのポインター、
は配列とポインターの関係に従って、2番目の
サブ配列も表します。 arr [1]、
 
 *(arr + 1)+2:
は、2番目のサブ配列の3番目の要素へのポインターです。
 
 *(*(arr + 1)+2):
は、2番目のサブ配列の3番目の要素の値を取得します。
と同じ arr [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