配列へのポインタ |配列ポインタ

配列へのポインタ |配列ポインタ

前提条件: ポインターの概要

次のプログラムを考えてみましょう。

C




#include> int> main()> {> > int> arr[5] = { 1, 2, 3, 4, 5 };> > int> *ptr = arr;> > printf> (> '%p '> , ptr);> > return> 0;> }>

上記のプログラムでは、ポインターがあります。 ptr それは0を指します 番目 配列の要素。同様に、配列の 1 つの要素だけではなく、配列全体を指すことができるポインターを宣言することもできます。このポインタは、多次元配列について話すときに役立ちます。

構文:

data_type  (* var_name ) [size_of_array]; 

ここ:

    data_type は、配列が保持するデータのタイプです。 var_name はポインタ変数の名前です。 size_of_array は、ポインタが指す配列のサイズです。

int (*ptr)[10]; 

ここ ptr 10 個の整数の配列を指すことができるポインタです。添字は間接演算よりも優先順位が高いため、間接演算子とポインタ名を括弧で囲む必要があります。ここでの ptr の型は「10 個の整数の配列へのポインタ」です。

注: 0 を指すポインタ 番目 配列の要素と配列全体を指すポインタは全く異なります。次のプログラムはこれを示しています。

C




// C program to understand difference between> // pointer to an integer and pointer to an> // array of integers.> #include> int> main()> {> > // Pointer to an integer> > int> *p;> > > // Pointer to an array of 5 integers> > int> (*ptr)[5];> > int> arr[5];> > > // Points to 0th element of the arr.> > p = arr;> > > // Points to the whole array arr.> > ptr = &arr;> > > printf> (> 'p = %p, ptr = %p '> , p, ptr);> > > p++;> > ptr++;> > > printf> (> 'p = %p, ptr = %p '> , p, ptr);> > > return> 0;> }>

出力

p = 0x7fff6463e890, ptr = 0x7fff6463e890 p = 0x7fff6463e894, ptr = 0x7fff6463e8a4 

ここ、 p 0へのポインタです 番目 配列の要素 到着しました 、 その間 ptr 配列全体を指すポインタです 到着しました

  • 基本タイプの p は int ですが、基本型は ptr は「5 つの整数の配列」です。
  • ポインタの算術演算は基本サイズを基準にして実行されることがわかっているため、ptr++ を記述すると、ポインタの演算が実行されます。 ptr 20 バイト前方にシフトされます。

ポインタ p と ptr を次の図に示します。濃い色の矢印は配列へのポインタを示します。

ポインタ式を逆参照すると、そのポインタ式が指す値を取得します。配列へのポインタは配列を指すため、それを参照解除すると配列を取得する必要があり、配列の名前はベース アドレスを示します。したがって、配列へのポインターが逆参照されるたびに、そのポインターが指す配列のベース アドレスを取得します。

C




// C program to illustrate sizes of> // pointer of array> #include> int> main()> {> > int> arr[] = { 3, 5, 6, 7, 9 };> > int> *p = arr;> > int> (*ptr)[5] = &arr;> > > printf> (> 'p = %p, ptr = %p '> , p, ptr);> > printf> (> '*p = %d, *ptr = %p '> , *p, *ptr);> > > printf> (> 'sizeof(p) = %lu, sizeof(*p) = %lu '> ,> > sizeof> (p),> sizeof> (*p));> > printf> (> 'sizeof(ptr) = %lu, sizeof(*ptr) = %lu '> ,> > sizeof> (ptr),> sizeof> (*ptr));> > return> 0;> }>

出力

p = 0x7fff55adbff0, ptr = 0x7fff55adbff0 *p = 3, *ptr = 0x7fff55adbff0 sizeof(p) = 8, sizeof(*p) = 4 sizeof(ptr) = 8, sizeof(*ptr) = 20 

多次元配列へのポインタ

1. ポインタと 2 次元配列

2 次元配列では、2 つの添え字を使用して各要素にアクセスできます。最初の添え字は行番号を表し、2 番目の添え字は列番号を表します。 2 次元配列の要素には、ポインター表記を使用してアクセスすることもできます。 arr が 2 次元配列であると仮定すると、任意の要素にアクセスできます arr[i][j] ポインタ式を使用した配列の *(*(arr + i) + j) 。次に、この式がどのように導出されるかを見てみましょう。
二次元配列を考えてみましょう arr[3][4] :

int arr[3][4] = { {1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12} }; 

コンピュータのメモリは線形に構成されているため、2 次元配列を行と列に格納することはできません。行と列の概念は理論上のものにすぎません。実際には、2 次元配列は行優先の順序で格納されます。つまり、行は互いに隣り合って配置されます。次の図は、上記の 2 次元配列がメモリにどのように格納されるかを示しています。

各行は 1 次元配列と考えることができるため、2 次元配列は、次々に配置された 1 次元配列の集合と考えることができます。つまり、二次元の配列が次々と配置されていると言えます。それでここで 到着しました は 3 つの要素の配列で、各要素は 4 つの整数の 1 次元配列です。
配列の名前は 0 を指す定数ポインタであることがわかっています。 番目 1 次元配列であり、アドレス 5000 が含まれます。 到着しました は「4 つの整数の配列へのポインタ」です。ポインタ演算によれば、式 arr + 1 はアドレス 5016 を表し、式 arr + 2 はアドレス 5032 を表します。
したがって、次のように言えます 到着しました 0を指す 番目 1 次元配列、 arr + 1 1を指します セント 1 次元配列と arr+2 2を指します nd 1 次元配列。

一般に、次のように書くことができます。

 arr + i Points to ith element of arr ->i 番目の 1 次元配列を指します> 
  • arr + i が i を指しているため、 番目 の要素 到着しました 逆参照すると、私が取得されます 番目 の要素 到着しました これはもちろん 1 次元配列です。したがって、次の式は *(arr + i) i のベースアドレスを与えます 番目 1 次元配列。
  • ポインタ式はご存知です *(arr + i) 添字式と同等です 到着しました 。それで *(arr + i) それは同じです 到着しました i のベースアドレスを与えます 番目 1 次元配列。
  • 2 次元配列の個々の要素にアクセスするには、任意の j にアクセスできる必要があります。 番目 i の要素 番目 1 次元配列。
  • ベースタイプなので、 *(arr + i) 整数 0 のアドレスが含まれています 番目 i の要素 番目 1 次元配列の場合、i 内の後続の要素のアドレスを取得できます。 番目 整数値を加算することによる 1 次元配列 *(arr + i)
  • 例えば *(arr + i) + 1 1のアドレスを表します セント 1の要素 セント i の要素 番目 1 次元配列と *(arr+i)+2 2のアドレスを表します nd i の要素 番目 1 次元配列。
  • 同様に *(arr + i) + j は j のアドレスを表します 番目 i の要素 番目 1 次元配列。この式を逆参照すると、j を取得できます。 番目 i の要素 番目 1 次元配列。

ポインタと 3 次元配列

int arr[2][3][2] = { {{5, 10}, {6, 11}, {7, 12}}, {{20, 30}, {21, 31}, {22, 32}} }; 

3 次元配列では、3 つの添え字を使用して各要素にアクセスできます。 3 次元配列を考えてみましょう。3 次元配列は 2 次元配列の配列であると考えることができます。つまり、3 次元配列の各要素は 2 次元配列であると考えられます。 3次元配列 到着しました は、各要素が 2 次元配列である 2 つの要素で構成される配列として考えることができます。配列の名前 到着しました 0へのポインタです 番目 2 次元配列。

したがって、ポインタ式は *(*(*(arr + i ) + j ) + k) は、添え字式 arr[i][j][k] と同等です。
式 *(arr + i) は arr[i] と同等であり、式 *(*(arr + i) + j) は arr[i][j] と同等であることがわかっています。したがって、arr[i] は i のベースアドレスを表すと言えます。 番目 2 次元配列と arr[i][j] は j のベース アドレスを表します。 番目 1 次元配列。

以下の例は、ポインターを使用して 3D 配列の要素を出力するプログラムを示しています。

C




// C program to print the elements of 3-D> // array using pointer notation> #include> int> main()> {> > int> arr[2][3][2] = {> > {> > {5, 10},> > {6, 11},> > {7, 12},> > },> > {> > {20, 30},> > {21, 31},> > {22, 32},> > }> > };> > int> i, j, k;> > for> (i = 0; i <2; i++)> > {> > for> (j = 0; j <3; j++)> > {> > for> (k = 0; k <2; k++)> > printf> (> '%d '> , *(*(*(arr + i) + j) +k));> > printf> (> ' '> );> > }> > }> > return> 0;> }>

出力

5 10 6 11 7 12 20 30 21 31 22 32 

次の図は、上記のプログラムで使用される 3 次元配列がメモリにどのように格納されるかを示しています。

配列へのポインタの添字

仮定する 到着しました は 3 行 4 列の 2 次元配列であり、 ptr 4 つの整数の配列へのポインタであり、 ptr 配列のベースアドレスが含まれます 到着しました

int arr[3][4] = {{10, 11, 12, 13}, {20, 21, 22, 23}, {30, 31, 32, 33}}; int (*ptr)[4]; ptr = arr; 

以来 ptr 最初の行の 2 次元配列、つまり 4 つの整数の配列へのポインターです。 ptr+i 私を指すでしょう 番目 行。逆参照について ptr+i 、iのベースアドレスを取得します 番目 行。 jのアドレスにアクセスするには 番目 i の要素 番目 行ポインタ式に j を追加できます *(ptr + i) 。したがって、ポインタ式は *(ptr + i) + j jのアドレスを与える 番目 i の要素 番目 行とポインタ式 *(*(ptr + i)+j) j の値を与えます 番目 i の要素 番目 行。
ポインタ式 *(*(ptr + i) + j) は添字式 ptr[i][j] と同等であることがわかります。したがって、2 次元配列のベース アドレスを含むポインタ変数がある場合、そのポインタ変数を二重に添え字にすることで配列の要素にアクセスできます。

C




// C program to print elements of a 2-D array> // by scripting a pointer to an array> #include> int> main()> {> > int> arr[3][4] = {> > {10, 11, 12, 13},> > {20, 21, 22, 23},> > {30, 31, 32, 33}> > };> > int> (*ptr)[4];> > ptr = arr;> > printf> (> '%p %p %p '> , ptr, ptr + 1, ptr + 2);> > printf> (> '%p %p %p '> , *ptr, *(ptr + 1), *(ptr + 2));> > printf> (> '%d %d %d '> , **ptr, *(*(ptr + 1) + 2), *(*(ptr + 2) + 3));> > printf> (> '%d %d %d '> , ptr[0][0], ptr[1][2], ptr[2][3]);> > return> 0;> }>

出力

0x7ffc9556b790 0x7ffc9556b7a0 0x7ffc9556b7b0 0x7ffc9556b790 0x7ffc9556b7a0 0x7ffc9556b7b0 10 22 33 10 22 33