C言語の多次元配列へのポインタの説明とサンプルコードです。
1. 配列要素へのポインタ
最初は導入です。C言語の入門書にも登場する配列とポインタの関係を説明します。
1.1. 一次元配列とポインタの組み合わせ
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
#include <stdio.h> #define NUMBER_OF_COLUMNS 4 int main(void) { int linear_array_x[NUMBER_OF_COLUMNS] = { 1, 2, 3, 4}; int linear_array_y[NUMBER_OF_COLUMNS] = { 11, 12, 13, 14}; int *top_of_columns; top_of_columns = linear_array_x; printf("top_of_columns[1] = %d\n", top_of_columns[1]); /* 2 */ printf("top_of_columns[3] = %d\n", top_of_columns[3]); /* 4 */ top_of_columns = linear_array_y; printf("top_of_columns[1] = %d\n", top_of_columns[1]); /* 12 */ printf("top_of_columns[3] = %d\n", top_of_columns[3]); /* 14 */ return 0; } |
もっともシンプルな例です。整数型の1次元配列を定義して、配列の先頭要素へのアドレスをポインタ変数 int *top_of_columns
に代入しています。「ポインタ top_of_columns は一次元配列の先頭要素を指している」ということを明確にするために、2つの一次元配列を用意して、途中で指している一次元配列を書き換えています。
1.2. 二次元配列とポインタの組み合わせ
最初の例では『1次元配列の先頭要素(=整数型変数)へのポインタ』であることを強調するために1次元配列を2個用意しました。しかし、よくあるパターンは下記の例のように2次元配列を用意して、2次元配列の一部である『1次元配列の先頭要素のアドレス』をポインタ変数に代入する実装だと思います。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
#include <stdio.h> #define NUMBER_OF_ROWS 2 #define NUMBER_OF_COLUMNS 4 int main(void) { int square_array[NUMBER_OF_ROWS][NUMBER_OF_COLUMNS] = { { 1, 2, 3, 4}, { 11, 12, 13, 14} }; int *top_of_columns; top_of_columns = square_array[0]; printf("square_array[0][1] = %d\n", top_of_columns[1]); /* 2 */ top_of_columns = square_array[1]; printf("square_array[1][1] = %d\n", top_of_columns[1]); /* 12 */ return 0; } |
整数型1次元配列の先頭要素は整数型変数です。だから、その先頭要素へのポインタ変数は int *top_of_columns
で辻褄が合っています。そして先頭要素から +1 オフセットした要素を参照するために上記のコードでは top_of_columns[1]
という書き方をしています。 *(top_of_columns + 1)
という書き方をしても同じ意味になります。
2. 一次元配列へのポインタ
ここからが本題となります。C言語で1次元配列へのポインタをどのように記述するかをサンプルコードを示しながら説明します。
2.1. 一次元配列へのポインタ
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
#include <stdio.h> #define NUMBER_OF_ROWS 2 #define NUMBER_OF_COLUMNS 4 int main(void) { int square_array_x[NUMBER_OF_ROWS][NUMBER_OF_COLUMNS] = { { 1, 2, 3, 4}, { 11, 12, 13, 14} }; int square_array_y[NUMBER_OF_ROWS][NUMBER_OF_COLUMNS] = { { 21, 22, 23, 24}, { 31, 32, 33, 34} }; int (*top_of_rows)[NUMBER_OF_COLUMNS]; int *top_of_columns; top_of_rows = square_array_x; top_of_columns = top_of_rows[1]; printf("square_array_x[1][1] = %d\n", top_of_columns[1]); /* 12 */ top_of_rows = square_array_y; top_of_columns = top_of_rows[1]; printf("square_array_y[1][1] = %d\n", top_of_columns[1]); /* 32 */ return 0; } |
1次元配列へのポインタ変数は int (*top_of_rows)[NUMBER_OF_COLUMNS]
というふうに記述します。 ポインタ変数 top_of_rows
はただ一つ定義されます。 「NUMBER_OF_COLUMNS
個の要素をもつ1次元配列へのポインタ変数」という意味になります。
2.2. ポインタ配列
次の例のように変数定義の括弧を省略すると『配列へのポインタ』ではなく『ポインタの配列』の意味になります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
#include <stdio.h> #define NUMBER_OF_ROWS 2 #define NUMBER_OF_COLUMNS 4 int main(void) { int square_array[NUMBER_OF_ROWS][NUMBER_OF_COLUMNS] = { { 1, 2, 3, 4}, { 11, 12, 13, 14} }; int *top_of_columns[NUMBER_OF_ROWS]; top_of_columns[0] = square_array[0]; top_of_columns[1] = square_array[1]; printf("square_array[0][1] = %d\n", top_of_columns[0][1]); /* 2 */ printf("square_array[1][1] = %d\n", top_of_columns[1][1]); /* 12 */ return 0; } |
int top_of_columns[0]
と int top_of_columns[1]
の2個の要素をもつポインタ変数配列を定義しています。そして上記の例では、2個のポインタ変数に『(たまたま)異なる2個の変数(配列の先頭要素)へのアドレス』を代入しています。複数のポインタ変数が異なる変数を指しても、ただ1個の変数を指しても構いません。上記のサンプルコードはシンプルな内容を持って回った書き方をしているため、ポインタ変数配列を使用する旨味はありませんが、あくまでもポインタ変数の使い方次第です。
2.3. 三次元配列と一次元配列へのポインタの組み合わせ
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
#include <stdio.h> #define NUMBER_OF_PAGES 2 #define NUMBER_OF_ROWS 3 #define NUMBER_OF_COLUMNS 4 int main(void) { int cubic_array[NUMBER_OF_PAGES][NUMBER_OF_ROWS][NUMBER_OF_COLUMNS] = { { { 1, 2, 3, 4}, { 11, 12, 13, 14}, { 21, 22, 23, 24}, }, { {101,102,103,104}, {111,112,113,114}, {121,122,123,124}, }, }; int (*top_of_rows)[NUMBER_OF_COLUMNS]; int *top_of_columns; top_of_rows = cubic_array[0]; top_of_columns = top_of_rows[1]; printf("square_array[0][1][1] = %d\n", top_of_columns[1]); /* 12 */ top_of_rows = cubic_array[1]; top_of_columns = top_of_rows[1]; printf("square_array[1][1][1] = %d\n", top_of_columns[1]); /*112 */ return 0; } |
ここまで複雑になると何がなんやらですが、上記の例では3次元配列を使い2次元配列を2個定義しています。そして2次元配列の先頭要素(=1次元配列へのポインタ)を top_of_rows
に代入しています。 top_of_rows
はポインタ変数がただ一つ。 top_of_columns
もポインタ変数がただ一つです。そこで異なる配列要素を参照するために値の上書きをおこなっています。ポインタ変数はただ一つ。ただ一つのポインタ変数が「こちらの要素」を指したり、「あちらの要素」を指したり、と指す(ポインティングする)対象を上書きで変更しています。
3. 二次元配列へのポインタ
下記が3次元配列を2個定義して、2次元配列へのポインタに代入する例です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
#include <stdio.h> #define NUMBER_OF_PAGES 2 #define NUMBER_OF_ROWS 3 #define NUMBER_OF_COLUMNS 4 int main(void) { int cubic_array_x[NUMBER_OF_PAGES][NUMBER_OF_ROWS][NUMBER_OF_COLUMNS] = { { { 1, 2, 3, 4}, { 5, 6, 7, 8}, { 9, 10, 11, 12}, }, { { 13, 14, 15, 16}, { 17, 18, 19, 20}, { 21, 22, 23, 24}, } }; int cubic_array_y[NUMBER_OF_PAGES][NUMBER_OF_ROWS][NUMBER_OF_COLUMNS] = { { {101,102,103,104}, {105,106,107,108}, {109,110,111,112}, }, { {113,114,115,116}, {117,118,119,120}, {121,122,123,124}, } }; int ((*top_of_array)[NUMBER_OF_ROWS])[NUMBER_OF_COLUMNS]; int (*top_of_page)[NUMBER_OF_COLUMNS]; int *top_of_row; top_of_array = cubic_array_x; top_of_page = top_of_array[1]; top_of_row = top_of_page[1]; printf("array_x[1][1][1] = %d\n", top_of_row[1]); top_of_array = cubic_array_y; printf("array_y[1][1][1] = %d\n", top_of_array[1][1][1]); return 0; } |
int ((*top_of_array)[NUMBER_OF_ROWS])[NUMBER_OF_COLUMNS]
と記述することで『2次元配列(の先頭)へのポインタ』を定義することができます。括弧の位置が重要です。括弧を省略してしまうと『配列へのポインタ』ではなく『ポインタの配列』になります。4次元配列を定義して2次元配列へのポインタに代入することもできますが、配列定義が煩雑になるためサンプルコードは省きます。
以上、『配列へのポインタ』の説明でした。