配列は、ポインタを使ってデータを取得することができます。逆にポインタに対して、配列でアクセスしようと思えばできます。
では、配列とポインタのどっちを使うほうがいいのか?ということになりますが、ややこしくなくて済むのは配列でしょう。
しかし、関数には配列を渡したり、配列を戻り値にすることはできませんから、ポインタを使うことになるでしょう。
ここでは、私が思う配列とポインタのどっちを使ったほうがいいかを書いていこうと思います。
通常は配列を使うべし
ポインタを使わなくてすむのなら、使わないほうがいいでしょう。わざわざ、配列ですべてプログラムが書けるのに、無理にポインタを使うのは間違いを引き起こすだけです。
ポインタは使い方を間違えると、全然違うアドレスにアクセスしてしまったり、アクセスしてはいけないようなアドレスに、アクセスしてしまったりしかねません。
ポインタを扱うときは慎重にならなければなりません。直感で、プログラム内の変数の値の変化がつかみにくいというのもあります。
したがって、配列ですべてが完結できるのであれば、配列でプログラムを書くべきです。”[]”で囲われた数値の要素のデータに、確実にアクセスできるからです。
これなら、配列の範囲以外の値を、指定しないようにさえ気を付けるだけで済むのは、おわかりただけますよね。
例えば、配列に値を入れて、そのデータを全て足すだけのようなプログラムだと、ポインタを使う必要は全くないですね。
#include <stdio.h> void main(void) { int array[5]; int i; int sum; printf("データ入力\n"); for (i = 0; i < 5; i++) { printf("array[%d] = ", i); scanf("%d", &array[i]); } sum = 0; for (i = 0; i < 5; i++) { sum += array[i]; } printf("データ合計\n"); printf("sum = %d\n", sum); }
これで済みます。これをわざわざ、
#include <stdio.h> void main(void) { int array[5]; int i; int sum; printf("データ入力\n"); for (i = 0; i < 5; i++) { printf("array[%d] = ", i); scanf("%d", array + i); } sum = 0; for (i = 0; i < 5; i++) { sum = sum + *(array + i); } printf("データ合計\n"); printf("sum = %d\n", sum); }
なんてことすると、ややこしいし、間違いが起こりそうなのはわかりますよね。
なので、私はできる限り配列で済むのなら、ポインタを使わないで、プログラムを書いてほしいと思うのです。
関数に渡すときはポインタを使うしかない
配列を直接、関数に渡すことはできません。この場合は、配列の先頭アドレスを関数に渡して、ポインタで処理するしかありません。
関数にはポインタで渡すことはわかったが、関数内の処理は配列で書くのか、ポインタで書くのかという判断ですが、私はポインタで書くべきなのかなと思います。
なぜなら、関数にとって、引数で渡されてきたポインタは配列の先頭アドレスなのか、単なる変数のポインタなのか、わからないからです。
通常、関数に配列を渡したいと思った場合は、要素数も引数で渡されると思うので、配列で表現することも可能とは思います。
関数だけを見たとき、引数のポインタがいきなり配列で表現されていたら、びっくりしませんか?
int Calculation(int* pt) { int i; int sum; sum = 0; for (i = 0; i < 5; i++) { sum += pt[i]; } return sum; }
なんとなく、違和感を感じませんか?この関数だけ見ると、int型のポインタが引数になっているとしか読めないからです。
配列の先頭アドレスとは、思わないでしょう。
こういう場合は、引数をこうすると、配列ということがわかると思います。
int Calculation(int pt[]) { int i; int sum; sum = 0; for (i = 0; i < 5; i++) { sum += pt[i]; } return sum; }
"p[]"で配列の先頭ポインタを渡すということを明示しておいてあげれば、わかりやすくなりますね。配列の先頭アドレスを渡しているので、関数内で配列の要素の値を変更すると、当然、呼び出し元の配列の要素の値も変更されてしまいます。
前者のほうは、
int Calculation(int* pt) { int i; int sum; sum = 0; for (i = 0; i < 5; i++) { sum = sum + *(pt + i); } return sum; }
としたほうが、私はしっくりきます。
for文の繰り返し回数は5と固定していますが、これも引数で渡すようにしてあげるほうが、いいでしょう。もし、配列の要素数が5未満なら、思うような結果が得られないからです。
構造体を配列で扱うかポインタを使ってリスト構造で扱うか
構造体を配列にして扱うのか、構造体のメンバに自己参照型のポインタを持って、リスト構造化してあつかうのかというものです。
どちらでもいいとは思いますが、データの追加、削除がないような構造体のデータ群を扱うのなら、構造体の配列で十分だし、データの追加、削除を行うようなものなら、リスト構造化したほうが有利です。
まず、データの追加、削除がないのなら、わざわざ、ややこしいリスト構造化をやる必要性はありません。なぜなら、配列で必要なデータを十分アクセスできるからです。
ですが、データの追加や削除を行うようなものなら、配列では扱いにくいです。扱いにくいだけで、扱えないわけではありません。
ただ、データの追加や削除を行うときに、追加や削除するデータの位置によって、配列のデータを1つずつずらす必要がでてきます。これは非常に面倒だし、処理時間もかかります。
一方で、リスト構造化しておくと、追加や削除が容易になります。それは、構造体メンバに持っている自己参照型ポインタに格納されているアドレスを変更するだけだからです。
リスト構造化については、こちらの記事をお読みください。
構造体だけでも、ややこしいですが、これの配列やポインタも登場してしまうので、1つ1つの内容は最低でも理解しておきましょう。
そうでなければ、組み合わさったとき、訳が分からなくなります。
1つ1つの内容を理解しておけば、組み合わさっても、扱っていけるようになります。
まとめ
私が思う配列とポインタでどっちを使うほうがいいかを書きました。まぁ要するに、私はポインタが嫌いということです。嫌いでもプログラマーをやってます。
ポインタも一応扱えますが、やっぱり嫌ですね。できる限り、ポインタを使わないで済まそうとしますよ。でも、やっぱり必要だし、便利なんですね。
便利なんで使わない手はないんですが、扱いを間違えると、動きがおかしくなりますから慎重になってしまうのです。
慎重になりすぎて、プログラム作成が進まないのはダメですから、慎重かつスピーディーにやっていく必要があるのです。
したがって、あなたもそうかもしれませんが、私はおおまかに、ここに書いたように、配列とポインタの関係において、ポインタは関数でのやりとりだけ、それ以外は配列でという使い分けです。
ポインタは配列との使い分けでなく、いろんな使い方ができてしまいます。できてしまうので、本当に慎重に扱うべきだと思います。
リスト構造は組み込みマイコンでは、ほぼ使いません。なので、あなたが組み込みマイコンのプログラマーになるのなら、リスト構造のことは忘れてしまっても問題ありません。
配列とポインタの基礎さえあれば、プログラムは作れます。がんばってやっていきましょう。