10.ポインタ

10-12. 二次元配列をポインタからアクセスする

どうしてもこの前の2次元配列をポインタでアクセスしたい。
また、この配列を他の関数からアクセスさせたい。

というところから続きます。
まず、問題になっているプログラムを再度、掲載してみます。

main()
{
    int matrix[2][3]={{1,2,3},{4,5,6}};
    int **point;
    point=matrix;
    printf("matrix[0][0] = %d\n",**point        );
    printf("matrix[0][1] = %d\n",*(*point+1)    );
    printf("matrix[0][2] = %d\n",*(*point+2)    );
    printf("matrix[1][0] = %d\n",**(point+1)    );
    printf("matrix[1][1] = %d\n",*(*(point+1)+1));
    printf("matrix[1][2] = %d\n",*(*(point+1)+2));
}

◇2次元配列をポインタからアクセスする

一つの解決法として、ポインタへ配列全体の先頭アドレスを渡す方法があります

前回のようにポインタのポインタでは失敗したので、ポインタにしようと言うことになります。

#include<stdio.h>

main()
{
    int matrix[2][3]={{1,2,3},{4,5,6}};
    int *point;
    point=matrix;
    printf("matrix[0][0] = %d\n",*(point)    );
    printf("matrix[0][1] = %d\n",*(point+1)  );
    printf("matrix[0][2] = %d\n",*(point+2)  );
    printf("matrix[1][0] = %d\n",*(point+3+0));
    printf("matrix[1][1] = %d\n",*(point+3+1));
    printf("matrix[1][2] = %d\n",*(point+3+2));
}

printf文では少し変わったことをしていますね。
そもそも、この方法ではコンパイル時に警告が出ます。
さらに2次元配列にアクセスできるようになっていてもポインタからアクセスする時は1次元配列のような扱いになっているのであまりよい例ではありません。

◇ポインタ配列を使う

今度はポインタ配列をつかった方法でやってみましょう。

#include<stdio.h>

main()
{
    int matrix[2][3]={{1,2,3},{4,5,6}};
    int *point[2];
    point[0]=matrix[0];
    point[1]=matrix[1];
    printf("matrix[0][0] = %d\n",*(point[0])  );
    printf("matrix[0][1] = %d\n",*(point[0]+1));
    printf("matrix[0][2] = %d\n",*(point[0]+2));
    printf("matrix[1][0] = %d\n",*(point[1]+0));
    printf("matrix[1][1] = %d\n",*(point[1]+1));
    printf("matrix[1][2] = %d\n",*(point[1]+2));
}

この方法が最も適していると思います。文字列の場合は特に便利です。
ポインタ配列の場合、行ごとに代入しないといけないのですが、文字列の場合は配列を使わなくてもポインタで初期化できるので前回のようにポインタ配列で初期化すると簡単に文字列の配列ができます。

上記より良い方法を読者の方から紹介していただきました。
心から厚く御礼申し上げます。

▼引用:一部編集▼━━━━━━━━━━━━━━━━━━━━━━━━━━◆

int 型の2次元配列をポインタでアクセスする場合、もちろんいろいろな手があるのですが

int matrix[2][3]={{1,2,3},{4,5,6}};
int *point[2];

が最も適しているとのことですが、私は異なる意見を有しています。

この int 型の2次元配列の場合

int (*x)[3];

という「int 型要素3個の配列へのポインタ」を定義してから配列のデータにアクセスするというのが最も適していると思われます。

また、特に文字列の場合

char str[16][128];
char (*sPtr)[128] = str;

としてやると sPtr[0] や sPtr[1] などはもちろんのこと、sPtr ++ などの演算も矛盾なく利用することができます。

▲ここまで▲━━━━━━━━━━━━━━━━━━━━━━━━━━━━━◆

これは、前回のポインタ配列を使った方法とは行と列が逆になった方法です。

また、サンプルプログラムまで添付していただきました。

#include <stdio.h>

int main()
{
    int data[][3] = {{1, 2, 3}, {4, 5, 6}};
    int (*dPtr)[3] = data;

    printf("data[0][0] = %d\n", dPtr[0][0]);
    printf("data[0][1] = %d\n", dPtr[0][1]);
    printf("data[0][2] = %d\n", dPtr[0][2]);
    printf("data[1][0] = %d\n", dPtr[1][0]);
    printf("data[1][1] = %d\n", dPtr[1][1]);
    printf("data[1][2] = %d\n", dPtr[1][2]);

    return 0;
}

私が紹介したものに比べ随分すっきりして読みやすいプログラムです。

ここで、ふと思ったのですが、配列をポインタのようにアクセスすること、つまり、間接アクセス演算子(*)を使ってアクセスはできましたが、この逆、ポインタを配列のような形式でアクセスできるのでは?

さっきのサンプルを書き換えてみます。

#include<stdio.h>

int main()
{
    int data[3] = {1, 2, 3};
    int *dPtr   = data;

    printf("data[0] = %d\n", dPtr[0]);
    printf("data[1] = %d\n", dPtr[1]);
    printf("data[2] = %d\n", dPtr[2]);

    return 0;
}

/* 結果
data[0] = 1
data[1] = 2
data[2] = 3
*/

期待どおりですね。

1.読者様より提供していただいたサンプル

#include <stdio.h>

int main()
{
    int data[][3] = {{1, 2, 3}, {4, 5, 6}};
    int (*dPtr)[3] = data;

    printf("data[0][0] = %d\n", dPtr[0][0]);
    printf("data[0][1] = %d\n", dPtr[0][1]);
    printf("data[0][2] = %d\n", dPtr[0][2]);
    printf("data[1][0] = %d\n", dPtr[1][0]);
    printf("data[1][1] = %d\n", dPtr[1][1]);
    printf("data[1][2] = %d\n", dPtr[1][2]);

    return 0;
}

これについて、ポインタ配列を使った方法と比較したいと思います。
まず提供していただいたものは『配列のポインタ』と解釈するのが自然だと思います。

メモリーのイメージ

dPtr data[2][3]
+—–+ +———–+
|&data|—–>| 1 | 2 | 3 |
+—–+ |———–|
| 4 | 5 | 6 |
+———–+

dPtrは要素が3つの配列のポインタである。
dPtr + 1 とすると data[1][0] のアドレスを示す。

2.ポインタ配列を使った方法のサンプルはこちら。

#include<stdio.h>

int main(){
    int matrix[2][3]={ {1, 2, 3}, {4, 5, 6} };
    int *point[2];
    point[0]=matrix[0];
    point[1]=matrix[1];

    printf("matrix[0][0] = %d\n",point[0][0]);
    printf("matrix[0][1] = %d\n",point[0][1]);
    printf("matrix[0][2] = %d\n",point[0][2]);
    printf("matrix[1][0] = %d\n",point[1][0]);
    printf("matrix[1][1] = %d\n",point[1][1]);
    printf("matrix[1][2] = %d\n",point[1][2]);

    return 0;
}

前回のサンプルに習って少し書き換えてあります。

メモリーのイメージ

point[2] matrix[2][3]
+——-+ +———–+
|data[0]|—–>| 1 | 2 | 3 |
+——-+ |———–|
|data[1]|—–>| 4 | 5 | 6 |
+——-+ +———–+

メモリーイメージでどちらが適当なのか一目瞭然ですね。

アクセスに関しては1の方法が適当だと思います。
2の場合はソートなどの処理に利用するのに適していると思います。