C言語の配列はなぜポインタのように使えるのか?メモリ構造から理解する

目次

はじめに

C言語を学んでいると、こんなコードをよく見ます。

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

さらに:

C
arr[1]

C
p[1]

が同じように使えます。

そのため、

初心者さん

配列ってポインタなの?

と思う人は非常に多いです。

しかし実際には、
配列とポインタは別のものです。

ではなぜ、配列はポインタのように使えるのでしょうか?

その理由は、「メモリ上での配置とアドレス計算」にあります。

この記事では、

  • 配列はメモリ上でどう並ぶのか
  • ポインタは何を保持しているのか
  • CPUはどうやって配列へアクセスしているのか
  • なぜ配列名がポインタのように扱われるのか

を、メモリ視点で整理していきます。


初心者さん

配列ってポインタなんですか?

エンジニアくん

実は違うよ。
ただ、配列名が“アドレスとして扱われる”場面があるんだ。

初心者さん

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

エンジニアくん

そう。
C言語は“メモリ”を見ると理解しやすいんだ。


メモリ領域解説シリーズ

本記事は「メモリ領域解説」シリーズの1つです。

メモリ領域解説シリーズの全体像はこちら

配列は「連続したメモリ領域」

まず、配列の本質を整理します。

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

これは、「int型を3個、連続して配置する」という意味です。

C言語での配列の書き方と仕組みはこちらの記事で解説しています。


メモリ上ではこう並ぶ

例えば int が4バイトなら、メモリ上では次のようになります。

配列のメモリ配置

重要なのは、メモリ上で連続していることです。

これが配列の本質です。


ポインタは「アドレスを保持する変数」

次にポインタです。

C
int *p;

これは、int型データのアドレスを保存する変数です。

例えば:

C
p = arr;

とすると、parr の先頭アドレスが入ります。

C言語でのポインタの書き方と仕組みはこちらの記事で解説しています。


メモリ上ではこうなる

  • arr → データそのもの
  • p → アドレスを保存
配列とポインタのメモリ配置

配列アクセスは内部でアドレス計算している

ここが重要です。

例えば:

C
arr[1]

は内部的には、

C
*(arr + 1)

として扱われています。


CPUはこう計算している

CPUは、

先頭アドレス + 要素サイズ × index

で位置を計算しています。

例えば:

arr = 0x1000
int = 4バイト

なら:

arr + 0 → 0x1000
arr + 1 → 0x1004
arr + 2 → 0x1008

になります。

つまり:

C
arr[1]

とは、

0x1004 にある値を読む

という意味です。


配列名は「先頭アドレス」として扱われる

C言語では、
多くの場面で配列名が先頭要素へのポインタとして扱われます。

例えば:

C
int *p = arr;

は実質:

C
int *p = &arr[0];

と同じ意味です。


なぜポインタのように使えるのか

つまり:

配列名

先頭アドレスへ変換

アドレス計算

という流れになっているため、

C
arr[1]

C
p[1]

も同じように使えるのです。

C言語での配列とポインタの文法的な違いはこちらの記事で解説しています。


array decay(配列からポインタへの変換)

この仕組みを:

array decay
(配列からポインタへの変換)

と呼びます。

つまりC言語では、配列名は必要に応じて先頭アドレスへ変換されます

これが、配列とポインタが混乱しやすい理由です。


スタック上ではどう存在しているのか

例えば:

C
void func(void) {
    int arr[3] = {1,2,3};
    int *p = arr;
}

の場合、スタック領域には次のように配置されます。

スタック領域のメモリ配置

重要なのは:

  • arr は実データ
  • p はアドレスを保存している変数

という点です。

スタック領域についてはこちらの記事で解説しています。


この仕組みは組み込み開発でも重要

この考え方は、
C言語の多くの仕組みにつながります。

例えば:

  • ポインタ演算
  • 多次元配列
  • バッファ処理
  • DMA
  • メモリマップ
  • レジスタアクセス

などです。

特に組み込み開発では、「どこに何が配置されるか」を理解することが非常に重要です。


メモリ視点で見るとC言語が理解しやすくなる

C言語は、メモリとの距離が近い言語です。

そのため、文法だけで理解しようとすると、混乱しやすくなります。

しかし、メモリ上でどう存在しているかを意識すると、

  • 配列
  • ポインタ
  • アドレス
  • ポインタ演算

がつながって理解できるようになります。


まとめ

配列がポインタのように使える理由は、配列名が先頭アドレスとして扱われるからです。

さらに、配列アクセスは内部的にアドレス計算で行われています。

C言語では、メモリ上でどう配置されるかを理解することが非常に重要です。

メモリ視点で見ることで、
配列・ポインタ・アドレス計算の理解が一気につながります。

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

メモリ領域については他の記事でも解説しています。

またポインタ・配列にかかわるC言語の文法についても当サイトでは解説しています。

エンジニアとして技術を学ぶことは重要ですが、
キャリアや副業についても同時に考える必要があります。

副業の現実や市場価値、今後のキャリア戦略については、
こちらの記事でまとめています。

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

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

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

この記事を書いた人

組み込みソフトエンジニアとして働きながら、
C言語・メモリ・ポインタなどの基礎から実務まで解説しています。

副業・キャリアについても実体験ベースで発信中です。

X・Qiita・noteでも発信しています。
X:更新情報・日常
Qiita:技術発信
note:キャリア・副業

▼まずはここから読むのがおすすめ
C言語文法シリーズ
メモリ領域解説シリーズ
割り込み処理解説シリーズ
ソフトウェア設計解説シリーズ
キャリアと副業ロードマップ

コメント

コメントする


目次