
C言語入門|配列がポインタになる3つの理由
「え、配列を渡しただけなのに、なんでポインタ扱いになるの?」
C言語を学んでいると、ここで一度は頭がフリーズします。
しかも厄介なのは、
- ソースコードにはポインタなんて書いてない。
- コンパイルエラーも出ない。
- でも動きは明らかにポインタ渡し。
という、静かに罠が仕掛けられている感じ。
でも安心してください。
これは設計ミスでもバグでもなく、C言語が最初から持っている仕様です。
今回は
「なぜ配列はポインタになるのか?」
その理由を 3つのからくり構文 に分けて、順番にほどいていきます。

まず確認しておきたいサンプルプログラム
最初に、配列を引数に取る関数を呼び出す、シンプルな例を見てみましょう。
プロジェクト名:10-3-1 ソースファイル名: sample10-3-1.c
#include <stdio.h>
void update(int data[4])
{
data[0] = 99;
}
int main(void)
{
int values[4] = {10, 20, 30, 40};
update(values);
printf("values[0]=%d\n", values[0]);
return 0;
}実行結果
values[0]=99関数 update の中で data[0] を書き換えただけなのに、
main関数の配列までしっかり書き換わっています。
ここからが本題です。
理由① 関数宣言の特殊構文
まず最初の仕掛けが、関数宣言そのものにあります。
C言語には、次のルールがあります。
特殊構文① 関数宣言の解釈
関数の引数や戻り値に配列型を指定すると、
ポインタ型を指定したものと見なされる。
つまり、先ほどの関数宣言はこう読まれます。
void update(int* data)
{
data[0] = 99;
}実質的に同じと見なされる引数宣言
| 書き方 | 実際の意味 |
|---|---|
| int data[4] | int* data |
| int data[] | int* data |
| int* data | int* data |
添え字の 4 は 見た目の安心材料であって、型としては無視されます。
「勝手に型を変えられてるのに警告なし!?」
そう思うのが普通ですが、これがC言語の仕様です。
理由② 配列変数評価の特殊構文
「引数がポインタ扱いなのはわかった。
でも、呼び出すときは配列をそのまま渡してるよね?」
ここで登場するのが、2つ目の仕掛けです。
特殊構文② 配列変数の評価
ソースコード上に書かれた配列変数名は、
配列の先頭要素のアドレスに化ける。
つまり、この呼び出しは
update(values);実際には、次と同じ意味になります。
update(&values[0]);普通の変数との違い
| 種類 | アドレスを渡す書き方 |
|---|---|
| 通常の変数 | &x |
| 配列 | 配列名だけでOK |
配列だけが、暗黙的にアドレスに変換される特別扱いなのです。
理由③ 添え字演算子の特殊構文
「でもさ、updateの中で data[0] って書いてるよ?
ポインタなのに添え字って使っていいの?」
はい、そこが3つ目の仕掛けです。
特殊構文③ 添え字演算子の評価
ポインタ p があるとき、次はすべて同じ意味になります。
| 書き方 | 意味 |
|---|---|
| p[0] | *p |
| p[1] | *(p + 1) |
| p[N] | *(p + N) |
つまり、関数内のこのコードは
data[0] = 99;実質的に、次と同じです。
*data = 99;イメージとして理解する
- data は 配列の先頭アドレス
- *data は その場所にある int
- data[0] も 同じ場所を指す
だから、配列っぽく書いていても、
中身は完全にポインタ操作なのです。
3つの特殊構文が同時に働いた結果
ここまでをまとめると、配列を関数に渡した瞬間に次が起きています。
| 段階 | 実際に起きていること |
|---|---|
| 宣言 | 配列引数はポインタに読み替え |
| 呼び出し | 配列名が先頭アドレスに変換 |
| 操作 | 添え字がポインタ演算に変換 |
この3点セットが揃った結果、
配列は必ずポインタ渡しになる
という挙動が完成します。
なぜ独立性が崩れるのか
ポインタ渡しの最大の特徴はこれです。
ポインタ渡しによる独立性の崩壊
呼び出し元と呼び出し先が
同じメモリ番地を共有する。
つまり、
- main関数の配列
- 関数内の data
は、同じ実体を見ています。
だから、関数内で書き換えると、
呼び出し元の配列も当然変わるのです。
丸暗記でいいもの、理解すべきもの
正直に言うと、
- 特殊構文①
- 特殊構文②
この2つは 仕様として覚えるしかありません。
でも、
- 特殊構文③(添え字とポインタの関係)
これは、理解しておくと一気にC言語が読みやすくなります。
ここを理解できると、
- 配列
- ポインタ
- 関数引数
が、一本の線でつながって見えるようになります。
