1 次元配列の場合 x=a[i]
は と同等であることはわかってx=*(a+i)
いますが、ポインターを使用して 2 次元配列の要素にアクセスするにはどうすればよいですか?
6 に答える
要約:として定義された多次元配列がある場合int [][]
、 は次x = y[a][b]
と同等ですx = *((int *)y + a * NUMBER_OF_COLUMNS + b);
ボーリングの詳細:
上記の(int *)
キャストはy
、その必要性が最初は直感的ではない可能性があるため、いくつかの説明に値します。なぜそこになければならないのかを理解するには、次のことを考慮してください。
C/C++ の型付きポインター演算では、スカラーによる加算/減算/インクリメント/デクリメントの際に、型付きポインターの値 (アドレス) が型のサイズ (バイト単位) によって常に調整されます。
多次元配列宣言の基本型(要素型ではなく、変数型) は、最終次元よりも 1 つ少ない次元の配列型です。
これらの後者 (#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][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!
テーブル
C の 2D 配列は連続した一連の行です (Pascal とは異なります)。
4 行 5 列の整数のテーブルを作成すると、次のようになります。
要素に到達する
次の方法で要素に到達できます。
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;
}
説明
これは double poiner 演算であるため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);
記憶の中
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).
以前の回答はすでに非常によく説明されています。私の理解に従ってポインター式をリストし、それらをarr[i][j]形式と比較します。
2 次元配列のポインター式: 配列名自体は最初のサブ配列へのポインタです。 到着: 最初のサブ配列の最初の要素ではなく、最初のサブ配列へのポインターになります 配列、配列とポインタの関係に応じて、それも表現します 配列自体、 arr+1 : 最初のサブ配列の 2 番目の要素ではなく、2 番目のサブ配列へのポインターになります 配列、 *(arr+1) : 2 番目のサブ配列の最初の要素へのポインターになります。 配列とポインタの関係により、秒も表すサブ配列、 arr[1] と同じ、 *(arr+1)+2 : 2 番目のサブ配列の 3 番目の要素へのポインターになります。 *(*(arr+1)+2) : 2 番目のサブ配列の 3 番目の要素の値を取得します。arr[1][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