配列とポインタの違いとは?C言語で混乱しやすい関係をわかりやすく解説

C言語では「配列とポインタは同じ」と言われることがあります。
しかし、配列とポインタは別物です。

確かに似た動きをしますが、違いを理解していないとバグの原因になります。

この記事では

  • 配列とポインタの違い
  • なぜ同じように見えるのか
  • 実務で重要なポイント

を分かりやすく解説します。

基本的なポインタの使い方についてはこちらの記事で解説しています。

またC言語の配列についてはこちらの記事で詳しく解説しています。


C言語ポインタ解説シリーズ

本記事は「C言語ポインタ解説シリーズ」の1つです。
C言語のポインタを、組み込み開発の視点も交えて解説します。

C言語ポインタ解説シリーズ一覧はこちら

目次

配列とポインタは同じではない

まずはよく見るコードです。

C
int arr[3] = {1, 2, 3};
int *p = arr;

このコードでは

  • arr → 配列
  • p → ポインタ

pには配列の先頭アドレスが入ります。

つまり

C
arr:
[1][2][3]

p:

[1][2][3]

このため同じように使えます。

C
printf("%d", arr[0]);
printf("%d", p[0]);

どちらも 1 になります。

初心者さん

同じように使えるなら同じでは?

エンジニアくん

配列はデータそのものなんだ

エンジニアくん

ポインタは場所を指してるだけ

初心者さん

なるほど、だから似てるだけなんですね


メモリ視点でのポインタや配列の扱いについてはこちらの記事で解説しています。

違い① sizeof の結果が違う

一番分かりやすい違いはこれです。

C
int arr[3];
int *p;

sizeof(arr);
sizeof(p);

結果はこうなります。

C
sizeof(arr) → 12
sizeof(p)   → 8

まず配列から見てみます。

C
int arr[3];

これは intが3つ並んだ配列 です。

int は通常 4 バイトなので

C
[ int ][ int ][ int ]
  4      4      4

合計は

4 + 4 + 4 = 12 バイト

つまり

C
sizeof(arr) = 12

になります。


ではポインタはどうでしょうか。

C
int *p;

これは

「intを入れる変数」ではなく
intがある場所を指す変数 です。

例えば

C
int a = 10;
int *p = &a;

このときメモリのイメージはこうなります。

C
a: 10
アドレス 1000

p: 1000

つまり

  • a は値を持つ
  • p は「場所(アドレス)」を持つ

という違いがあります。


ではこの「アドレス」は何バイトでしょうか?

アドレスはメモリ上の場所を表す番号です。

例えば

C
0x00007FF8A1234567

このような値になります。

この値を丸ごと保存する必要があるため、
ポインタは 8バイト になります。

つまり

C
sizeof(p) = 8

になります。


まとめると

配列

C
[データ][データ][データ]

ポインタ

C
[アドレス]

配列はデータそのものを持ち
ポインタは場所を持つ

この違いによって

C
sizeof(arr) → 12
sizeof(p)   → 8

という差が生まれます。

メモリの視点での配列とポインタの違いはこちらの記事で解説しています。


違い② 配列は代入できない

ポインタは代入できます。

C
int *p;
int a = 10;

p = &a;

しかし配列は代入できません。

C
int arr[3];
int b[3];

arr = b;   // エラー

配列は固定されたメモリだからです。

一方ポインタは指す先を変更できます。

C
int a = 10;
int b = 20;

int *p;
p = &a;
p = &b;
初心者さん

配列は動かせないんですね

エンジニアくん

ポインタは行き先を変えられる

初心者さん

だから別物なんですね


配列はポインタに変換される

ここが混乱の原因です。

配列は式の中で使うと、先頭アドレスに変換されます。

C
int arr[3];

int *p = arr;

これは実際には

C
int *p = &arr[0];

と同じです。

つまり

配列はポインタに変換される

だから同じように使えるのです。


関数引数ではポインタになる

関数の引数に配列を書くと

C
void func(int arr[])

これは実際には

C
void func(int *arr)

と同じです。

つまり関数の中では
配列ではなくポインタとして扱われます。

C
void func(int arr[])
{
    sizeof(arr);
}

この場合

配列サイズではなく
ポインタサイズになります。

ここは非常にバグになりやすいポイントです。


配列ポインタとは(配列全体を指すポインタ)

普通のポインタは「先頭要素」を指します。

C
int arr[3] = {1,2,3};

int *p = arr;

C
p → arr[0]

つまり

「1個目の要素を指す」


配列ポインタは違います。

C
int arr[3] = {1,2,3};

int (*p)[3] = &arr;

p → arr 全体

つまり

「配列そのものを指す」

これが配列ポインタです。


普通のポインタとの違い

普通のポインタ

C
int *p = arr;

指すもの

C
arr[0]

配列ポインタ

C
int (*p)[3] = &arr;

指すもの

arr[0], arr[1], arr[2] まとめて

sizeofで見ると分かりやすい

C
int arr[3] = {1,2,3};
int *p1 = arr;
int (*p2)[3] = &arr;

printf("%zun", sizeof(*p1));
printf("%zun", sizeof(*p2));

結果

4
12

意味

C
*p1 → int4byte
*p2 → int[3]    → 12byte

つまり

p1 → 要素を指す
p2 → 配列を指す


なぜこんな書き方になるのか

右から読むと分かります。

普通のポインタ

C
int *p

p は
→ int を指す


配列ポインタ

C
int (*p)[3]

p は
→ int[3] を指す

だから配列ポインタになります。


どこで使うの?

二次元配列で使われます。

C
int arr[2][3];

これは

[配列][配列]

です。

1行を指すには

C
int (*p)[3] = arr;

これが自然になります。

C
p → {1,2,3}
     {4,5,6}

p は1行を指す。


配列ポインタのまとめ

普通のポインタ

C
int *p

→ 要素を指す


配列ポインタ

C
int (*p)[3]

→ 配列全体を指す

これが違いです。

関連記事:char配列とポインタの違いも理解しよう

配列とポインタの関係を理解したら、次に押さえておきたいのが char配列とcharポインタの違い です。

特に文字列では、

C
char str1[] = "hello";
char *str2 = "hello";

の違いで混乱しやすくなります。

書き換え可否や文字列リテラルとの関係まで、図付きで解説しています。


まとめ

配列とポインタの違い

  • 配列はデータ本体
  • ポインタはアドレス
  • sizeof の結果が違う
  • 配列は代入できない
  • 配列は式の中でポインタに変換される
  • 関数引数ではポインタになる

配列とポインタは似ていますが
本質的には別物です。

配列・ポインタ学習におすすめの書籍

新・明解C言語 入門編

配列とポインタは、C言語の中でも特につまずきやすい部分です。
「アドレス」「配列との違い」「*& の意味」など、最初は混乱しやすいポイントが多くあります。

この書籍は、図やサンプルコードを使いながら丁寧に説明されているため、独学でも理解を進めやすい1冊です。

特に、

  • 配列とポインタの関係
  • 文字列とポインタ
  • 関数とアドレス
  • メモリを意識した考え方

を基礎から学びたい方におすすめです。

楽天ブックス
¥2,530 (2026/06/13 12:41時点 | 楽天市場調べ)

この記事が参考になった方へ

C言語ポインタ解説シリーズ一覧はこちらです。

またポインタを含むC言語の基本文法も一覧で整理しています。

技術に関するご相談・開発・自動化ツール作成・記事執筆などのご依頼も承っています。

小さなご相談からでもお気軽にご連絡ください。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

コメント

コメントする


目次