いまさら聞けない!C言語のポインタと配列の違い
配列とポインタは「似ている」のではなく
似た振る舞いをする瞬間があるだけです
この記事では
int a[5]とint *pの違いを本質から整理したい- sizeofや関数引数で混乱したことがある
- 配列はポインタの別名だと思っている
そんなモヤっとを、構造から整えていきます
まず、いきなり結論を言います
配列は「実体」
ポインタは「参照」
ここがすべてです
なぜ同じに見えるのか
int a[5] = {1,2,3,4,5};
int *p = a;どちらも
a[0]
p[0]と書けます
だから混乱します
しかし中身はまったく違います
配列の正体

a はこの領域そのものです
a は
「先頭アドレスを表す式」に変換されることはありますが、実体を持つ変数です
重要なのはここです
配列はメモリを“所有”しています
ポインタの正体

p の中身は「1000」という数値
つまりアドレスです
p 自体は、配列を持っていません
決定的な違い①:sizeof
int a[5];
int *p = a;
sizeof(a); // ?
sizeof(p); // ?64bit環境なら
- sizeof(a) → 20
- sizeof(p) → 8
になります
なぜか
- a は 5個の int を持つ実体
- p は 1つのアドレスを持つ変数
ここを説明できるかが理解の分かれ目です
決定的な違い②:代入できるか
int a[5];
int b[5];
a = b; // コンパイルエラー配列は代入できません
なぜなら
配列は「固定された実体」だからです
一方
int *p;
int *q;
p = q; // OKポインタはアドレスを書き換えられます
決定的な違い③:関数引数での挙動
void func(int arr[5]);これは実際には
void func(int *arr);と同じです
ここで
「ほら、配列はポインタじゃん」
と思いがちですが、違います
配列は
関数に渡された瞬間
ポインタに変換される
だけです
これを「配列の退化」と呼びます
関数呼び出し時の変換

実体は外側
関数内ではアドレスのみ
よくある誤解
配列名はポインタ変数である
これは誤りです
配列名は
- 左辺値になれない
- アドレスを書き換えられない
つまり
ポインタっぽい振る舞いをする式
であって
ポインタ変数ではありません
配列ポインタとポインタ配列
ここも混乱ポイントです
int (*p)[5]; // 配列へのポインタ
int *p[5]; // ポインタの配列まったく違います
前者は
5要素配列を指すポインタ
後者は
ポインタを5個持つ配列
宣言は右から読む
これを意識すると整理できます
実務視点での違い
設計の観点では
配列は
- サイズが固定
- 自動領域に置かれることが多い
- 所有権が明確
ポインタは
- 可変サイズ
- 動的確保
- 所有権設計が必要
配列は「箱」
ポインタは「接続」
役割が違います
まとめ
配列とポインタは似ています
しかし本質はまったく違います
配列は
- 実体
- 連続領域
- 固定サイズ
ポインタは
- アドレスを持つ変数
- 書き換え可能
- 実体を持たない
この違いを
sizeof・代入可否・関数引数
この3点で説明できれば
理解はかなり深い位置にあります