配列とRAM - メモリの仕組み
2025-08-17
配列がメモリ(RAM)にどう保存されるか学んだこと
最初は「配列って変数をまとめただけでしょ?」と思ってたけど、実際のメモリの仕組みを知ると、なぜ配列が高速なのか理解できた。
そもそもデータ構造って何?
データ構造は、コンピュータのRAM(Random Access Memory)にデータを効率的に保存する方法のこと。RAMは単に「メモリ」とも呼ばれる。
今使ってるパソコンには多分8GB(10⁹バイト)くらいのRAMがある。1バイトは8ビットでできてる。
配列がメモリに保存される仕組み
例えば [1, 3, 5]
という配列があるとする。でもコンピュータは0と1(ビット)しか理解できない。
# 整数の配列
arr = [1, 3, 5]
# 各数値のバイナリ表現(1バイトで表すと)
# 1 = 00000001
# 3 = 00000011
# 5 = 00000101
メモリアドレスの仕組みを理解した瞬間
整数は通常4バイト(32ビット)を使う。配列を保存すると:
メモリアドレス | 値
0x1000 | 1 (4バイト)
0x1004 | 3 (4バイト)
0x1008 | 5 (4バイト)
重要な発見:アドレスが4バイトずつ離れてる!これが「連続的」ってことか。
文字配列の場合
# 文字の配列
chars = ['A', 'B', 'C']
# 文字は1バイト(8ビット)
# メモリでは:
# 0x2000: 'A' (1バイト)
# 0x2001: 'B' (1バイト)
# 0x2002: 'C' (1バイト)
文字は1バイトだから、アドレスは1バイトずつ増える。
なぜ連続メモリが重要なのか
最初は「別にバラバラでもよくない?」と思ってた。でも理解したのは:
高速アクセス: インデックスから直接アドレスを計算できる
# arr[i]のアドレス = 先頭アドレス + (i × データサイズ) # arr[2]にアクセスしたい場合: # アドレス = 0x1000 + (2 × 4) = 0x1008
キャッシュ効率: CPUは近くのメモリも一緒に読み込むから、連続してると速い
Pythonで配列のメモリアドレスを見てみる
import sys
import array
# Python配列の要素のメモリ使用量を確認
arr = [1, 3, 5]
for i, val in enumerate(arr):
print(f"arr[{i}] = {val}, サイズ: {sys.getsizeof(val)}バイト")
# より低レベルなarray moduleを使う
int_array = array.array('i', [1, 3, 5]) # 'i'は符号付き整数
print(f"配列全体のサイズ: {int_array.itemsize * len(int_array)}バイト")
print(f"要素あたりのサイズ: {int_array.itemsize}バイト")
学んだこと・気づいたこと
- データ型によってメモリ使用量が違う: intは4バイト、charは1バイト
- アドレスの増分はデータサイズに依存: これで配列の要素に O(1) でアクセスできる
- メモリの連続性が性能に影響: キャッシュヒット率が上がる
- 高級言語では隠蔽されてる: PythonやJavaScriptではこの辺を意識しなくていいけど、知ってると理解が深まる
なぜこの知識が重要か
最初は「実装できればいいじゃん」と思ってたけど:
- アルゴリズムの理解: なぜ配列アクセスが O(1) なのか理解できる
- 最適化: メモリ局所性を意識したコードが書ける
- システム設計: 大規模システムでメモリ効率を考える時に必須
- 面接対策: 「なぜ配列は高速なの?」って聞かれた時に答えられる
実践での応用
# メモリ効率の良い二次元配列の作り方
import numpy as np
# NumPyは連続メモリを使うから高速
matrix = np.array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
# Pythonのリストのリストより高速
python_matrix = [[1, 2, 3],
[4, 5, 6],
[7, 8, 9]]
# NumPyの方がキャッシュ効率が良い
まとめ
- 配列は連続したメモリ領域に保存される
- だからインデックスアクセスが O(1) で超高速
- データ型のサイズを意識すると、メモリアドレスの動きが理解できる
- 普段は抽象化されてるけど、この知識があるとデバッグやパフォーマンス改善で役立つ
次は静的配列について詳しく見ていく
Tags:
配列
メモリ
RAM