
はじめてのC言語 | 第16回
同じ景色から見る、もうひとつの不思議
UNIX Cafe のカウンターで、
ミナちゃんがノートを開きながら、少し首をかしげています。
ミナちゃん先生……配列を関数に渡したら、なんだか挙動がおかしいんです。
C言語を学ぶ多くの人が、ここで一度、同じ壁にぶつかります。
- 関数の中で配列の値を書き換えたら、呼び出し元の配列まで変わってしまった。
- sizeofでサイズを調べたら、期待していた大きさにならなかった。
「ちゃんと配列を渡したはずなのに……?」
今日はその違和感の正体を、
これまでに学んできた知識をヒントに、もう一度見つめ直してみましょう。
まずは復習|関数に「値」を渡すとどうなる?
ではまず、比較のために、普通の変数の場合がどうだったか思い出してみましょう。
int型のような普通の変数を関数に渡した場合、
- 関数の中でその値を使うことができる
- しかし、中で値を変更しても、呼び出し元の変数は変わらない
というルールがありましたね。
これは、関数に渡されているのが変数そのものではなく、「値のコピー」だからです。
UNIX Cafeの例えで言うなら、これは調理場に「材料そのもの」を渡すのではなく、
単に「分量を書いたメモ」を渡しているような状態です。
メモの数字をいくら書き換えても、元の材料が変わることはありませんよね。
では、配列を関数に渡すと何が起きるのか
さて、いよいよ本題の配列です。
関数を定義するとき、引数に
int a[]と書かれているのを見ると、多くの人が直感的にこう考えてしまいます。
「なるほど、配列がまるごと渡されるんだな」と。
でも、ここに C言語らしい落とし穴 があります。
関数の中で見ているのは「箱」ではなかった
ここで、これまで何度も確認してきた、配列に関する大切なルールを思い出してみましょう。
- 配列は、メモリ上に「ずらりと並んだ箱」のようなもの
- でも、配列名が表しているのは、箱そのものではなく、先頭の箱の場所
でしたね。
このルールが分かれば、もう答えは見えたも同然です。
関数に配列を渡したときに実際に送られているのは、
- 配列そのものではなく
- その配列がどこにあるかという「場所の情報」
なのです。
「配列がポインタになる」という表現は、少し誤解を招くかもしれません。
C言語を学んでいると、こんな説明をよく耳にします。
「配列は、関数に渡すとポインタに変わるんだ」と。
この説明は間違いではありませんが、ここでは、少し見方を変えてこうお伝えします。
配列が、関数に渡された瞬間に何かに「変身」するわけではなく、
配列の名前は、式の中では最初から「場所」を示すものとして扱われる。
ただ、それだけのことなのです。
バックヤードの作業で例えるなら、棚にある商品そのものをカウンターに運んでくるのではなく、
その商品が置いてある棚の「ロケーション札」を、そっと手渡している状態です。
だから、関数の中で書き換えると、外の配列も変わる
これで、ミナちゃんが最初に抱いた疑問が解けますね。
関数の中で配列の中身を書き換えると、呼び出し元の配列まで変わってしまう。
この現象は、魔法でもなければ、もちろん事故でもありません。
理由はとてもシンプルです。
関数の中と外で、同じ一つの「棚」を見ているからです。
関数に渡された一枚の「ロケーション札」を、呼び出し元と関数の中、その両方が共有しているのです。
ですから、関数の中で棚の箱を一つ入れ替えれば、
外からその棚を見たときにも、当然その結果が反映されている。
考えてみれば、とても素直で当たり前の話だったのです。
sizeofの値が小さくなるのも、同じ理屈です
C言語の配列でよくある、もう一つの疑問。
「関数の中でsizeofを使うと、なぜか配列のサイズが小さくなってしまう」
もうお分かりですね。これも、理由はまったく同じです。
関数が受け取っているのは、箱がずらりと並んだ配列全体ではなく、あくまで「先頭の箱の場所」という情報だけ。
一枚の「ロケーション札」だけを渡されても、その棚に一体いくつの箱が並んでいるかなんて、分かりようがありませんよね。どこまでがその配列の範囲なのか、場所の情報だけでは判断できないのです。
そのため、関数の中でsizeofを実行しても、「配列全体の大きさ」は計算できません。
sizeofが教えてくれるのは、単に「場所の情報を記憶している変数(つまりポインタ)そのものの大きさ」だけなのです。
ミナちゃんの中で、点と点が線でつながった瞬間
そのとき、ミナちゃんはふっと顔を上げました。
その瞳には、今までの戸惑いではなく、確かな納得の色が浮かんでいます。
今までバラバラだと思っていた「配列」、「ポインタ」、そして「関数」。
それらが、本当はすべて同じ一つのルールで動いていたことに気づいたのです。
「そっか……。配列そのものを渡しているつもりだったけど、私が本当に渡していたのは、配列の“場所”だったんだ」
そのたった一つの気づきが、これまでモヤモヤしていた数々の不思議な現象を、一本のきれいな線で結びつけてくれたのです。
まとめ:関数に配列を渡すときに、本当に起きていること
C言語の配列と関数が織りなす、少し不思議な物語の核心は、とてもシンプルでした。
- まず、配列の名前が指し示しているのは、配列全体ではなく、あくまで「先頭の場所」であること。
- だから、関数に配列を渡したときに実際に送られるのも、その「場所」の情報だけ。
関数の中ではsizeofの値が変わり、まるで配列が別のものに「変身」したかのように見えていたかもしれません。
しかし、呼び出し元と関数が見ている「景色」は、最初からずっと同じだったのです。
配列が壊れてしまったわけでも、関数に何か特別な魔法があるわけでもありません。
ただ、私たちは「場所」を渡していた。
すべての現象は、このたった一つの、とても素直なルールから生まれていたのです。
次へ進む前に:残された、たった一つの疑問
ここまでの話で、配列を渡す本当の意味が分かりました。
そうすると、今度はこんな疑問が浮かんできませんか?
「配列の“場所”しか渡せないなら、肝心の『箱の数』は、どうやって関数に伝えればいいんだろう?」と。
次回は、配列という「場所の情報」と一緒に、もう一つ、何を渡すべきなのか。
その答えを、これまでと同じ景色の中から一緒に見つけていきましょう。
安心してください。もう新しく覚えることは、ほとんどありません。
これまで見てきた景色の中に、そのヒントは隠されていますから。
さらに学びたいあなたへ
📘 用途ごとに選ぶ Linux のおすすめ本











