月ノ書

いまさら聞けない!C言語のポインタと配列の違い

配列とポインタは「似ている」のではなく
似た振る舞いをする瞬間があるだけです

この記事では

そんなモヤっとを、構造から整えていきます

まず、いきなり結論を言います

配列は「実体」
ポインタは「参照」

ここがすべてです

なぜ同じに見えるのか

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環境なら

になります

なぜか

ここを説明できるかが理解の分かれ目です

決定的な違い②:代入できるか

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点で説明できれば

理解はかなり深い位置にあります