こんばんは・:*+.\( °ω° )/.:+
バーチャル高専女子の七瀬真冬です。
以前ポインタを解説する記事を書いたのですが、その時にとある凄腕プログラマさんから「初心者がつまずきやすいのって、ポインタと配列の関係とかその辺りじゃない?」という意見をいただきました。
なるほど…
確かに私も、ポインタと配列の関係が理解できなくて悩んだ記憶があります。
というわけで、今回のテーマは"ポインタと配列の関係"にするよ!!
1記事にまとめると非常に長くなる内容なので、前編/後編に分ける予定です。
この記事を読む前に、私が以前書いたポインタ解説記事を読んでおくことをオススメします!
【C言語】ポインタが分からない人のためのポインタ講座。 - まふゆちゃんの技術ブログ。
■ 本記事の対象読者
・ポインタ=アドレスだってことは理解できてるよ!
・ポインタと配列は何か関係あるって習ったけど、よく分かってないよ!
といった人たち向けの記事になるよ。
ポインタについて何も理解できてない人は、こちらを読んでからまた来てね♡
■ コードの実行環境
・OS:Windows 10
・開発環境:Visual Studio 2017
いつも通りです♡
■ 注意!!
わかりやすさを重視しているので、ちょっぴり正確性に欠ける内容があるかも!
本記事はあくまでも、初心者さんのポインタへ対する苦手意識を取り除くのが目的です。
ポインタに関する正確な情報の提供は他のサイトに任せるよ!!
あと、スマホ版のレイアウトでは若干ソースコードの表示が崩れるから、PC版での閲覧を推奨します!
スポンサーリンク
■ まずは配列から。
以下のコードを見てください。
char型の配列を宣言して、内容をprintfで表示しているだけのコードです。
#include <stdio.h> int main(void) { char charArray[] = "Mafuyu\0"; // 配列の宣言だよ。 printf("charArrayで表される文字列 :%s\n", charArray); // (1) printf("charArrayが存在するアドレス:%d\n", charArray); // (2) return 0; }
実行結果はこちら。
まずコード上の(1)。
"%s"のフォーマット指定子に、配列名charArrayをセットすることで文字列を表示することができます。
特に難しいことはしていません。
次にコード上の(2)。
フォーマット指定子は"%d"だね。これは10進数の整数を出力するフォーマットです。
(アドレスを出力するなら本当は"%p"を使ったほうがいいんだけど、値が16進数で表示されるよね。
私のブログでは16進数に馴染みのないプログラミング初心者さんでも読みやすい記事を提供したいので、敢えて"%d"を使っています。)
出力された値は、"9436476"だね。
実はコレ、メモリ上の配列charArrayの"先頭アドレス"です。
すなわち、"Mafuyu"の'M'が存在するアドレスが表示されているわけです。
ちなみにこの値はコンピュータさんが勝手に決めるので、毎回違う値になるよ。
図に表すとこんな感じ。
つまり、配列を宣言したら、その配列名が"配列の先頭アドレス"を示すっていうことになります!!
今回の場合は、配列名"charArray"が、配列の先頭アドレス(9436476)です。
これ、かなり大事なことだからね!!
もう一度言うよ!!
配列名は、"配列の先頭アドレス"なんです!!!!!
ちょっと納得いかないかもしれないけど、そういう決まりなんです。
文句が言いたかったらC言語を作った人に言ってください。
試しに、配列の先頭要素"charArray[0]"のアドレス、すなわち"&charArray[0]"を表示して、charArrayと比較してみようか。
#include <stdio.h> int main(void) { char charArray[] = "Mafuyu\0"; // 配列の宣言だよ。 printf("&charArray[0]で示されるアドレス:%d\n", &charArray[0]); printf("charArrayで示されるアドレス :%d\n", charArray); return 0; }
実行結果。
おんなじアドレスが出力されたね!!
つまり、charArray は &charArray[0]と全く同じで、どちらも配列の先頭アドレスを示しています。
ちなみに実際の開発現場では、"配列名=配列の先頭アドレス"っていうのは暗黙の了解みたいなもんで、みんな当たり前のように使います。
今回の例でいうと、配列の先頭アドレスを示したいときは"&charArray[0]"という書き方は使わずに、"charArray"を使います。
慣れておいてね。
"配列名は、配列の先頭アドレス"って3回復唱したら、
次に進んでもいいよ。
■ 配列を関数に渡したい… どうする?
ここで問題です。
main関数で宣言したchar型の配列(文字列)を、別の関数に渡して表示したい!!という場面があったとします。
どうやって実装すべきだろう?
正解のコードを以下に示すよ。
#include <stdio.h> void printName(char *name); int main(void) { char name[] = "Mafuyu Nanase\0"; printName(name); // 配列名(先頭アドレス)を渡すよ。(1) return 0; } void printName(char *name) // 引数の型はcharのポインタ、即ちアドレス。(2) { printf("私の名前は%sです!よろしくー!!\n", name); }
コード内の(1)を見てね。
ココで渡しているのは配列名nameなので、実際に渡されているのは配列の先頭アドレスになります。
続いてコード内の(2)。
printName関数、即ち名前の文字列を表示する関数だね。
引数の型はcharのポインタ、即ちアドレスだね。
つまり、これでchar型配列の先頭アドレスを受け取れるわけです。
で、最後にprintfで文字列を表示してます。
ちなみに"%s"っていうフォーマット指定子だけど、これはchar型のポインタ(アドレス)をセットすることで、終端文字(\0)までの文字列を自動的に表示してくれる仕組みになっているんだよ!!
なので、終端文字(\0)を入れ忘れたら、後ろに意味不明な文字列がくっついてしまう場合があります。
C言語で文字列を宣言するときは、明示的に\0を付けるクセをつけるといいかも。
実行結果はこちら。
まぁ要するに、配列を関数に渡したいときは
配列の先頭アドレス(つまり配列名)を渡せばいい
ということです!!覚えておいてね。
■ 理屈はわかったけど、なんで先頭アドレスなんて使うんだ??
配列名は先頭アドレス(ポインタ)であるっていうことはもう理解できたよね。
ただ、理屈は分かっていても
・「関数間で配列を受け渡すとき、なんで先頭アドレスなんか使うんだ?」
・「配列そのものを引数として渡せばいいんじゃないか?」
とか思ってしまうんじゃないかな。
ちなみに、C言語では配列そのものを引数で渡すことはできません。
C言語では、配列のように連続したデータを扱うときは、"配列のデータそのもの"ではなく、"配列の先頭アドレス"を使います。
そういう仕様なんです。
何故アドレスを渡すことでうまくいくのかというと、配列はデータがメモリ上の連続した領域に存在していることは分かっているので、先頭のアドレスさえわかれば、後ろに続くデータも簡単に参照できるからです。
例えば、さっき説明したprintfの"%s"。
これは、文字列の先頭アドレスをセットして文字列を表示するよね。
文字列のデータはメモリ上に連続していることが分かっています。
なので、先頭アドレスから順番にデータを見ていって1文字ずつ表示して、終端文字(\n)が見つかったら終了。
といったふうに文字列の表示ができるわけです。
スポンサーリンク
■ まとめ
前編で覚えてほしいのは以下3点です!!
・配列を宣言したら、その配列名は"配列の先頭アドレス(ポインタ)"を表すよ!!
・C言語では、関数に配列を渡したいときは先頭アドレス(ポインタ)を使うよ!
・配列のデータはメモリ上に連続しているのは分かっているから、先頭さえ分かれば
後ろのデータも簡単に参照できるんだよ!!
上記3点、しっかり覚えておいてね!!
後編では、
・ポインタの値を操作するとどうなる?
・char型以外のポインタは?
といったことについて書きたいなー、と思ってます。
つづく…・:*+.\( °ω° )/.:+
【C言語】ポインタと配列の関係。(後編) - まふゆちゃんの技術ブログ。