C言語 › C++ › 第16回 std::array
第16回 std::array ― 型安全な固定長配列
C の int a[5] は便利ですが、関数に渡すとサイズ情報が失われる (ポインタに減衰する)、境界チェックがない、= で代入できない、などの罠がありました。std::array はこれら全部を解決した「サイズを持ち運ぶ安全な配列 」です。vector と違いコンパイル時にサイズが決まる ので、ヒープ確保もなし。
このページで押さえること
✅ 最低限ここだけ覚える
std::array<型, 個数> で固定長配列
サイズはコンパイル時 に決まる
a.size() / a[i] は vector と同じ
関数に渡してもサイズが保たれる
⭐ 余裕があれば読む
C 配列との比較(ポインタ減衰)
vector との使い分け
constexpr 文脈で使える
a.fill(v) / a.swap(b)
目次
1. まず触ってみる ― std::array の作り方
2. C の生配列との違い
3. 基本操作(vector とほぼ同じ)
4. array vs vector vs 生配列
5. よくある使い方
1. まず触ってみる ― std::array の作り方
std::array は、サイズが最初から決まっている 配列 です。vector のように途中で伸び縮みしません。その代わり、ヒープ確保がなく、C 配列並みに速い 。しかも vector と同じメソッド(size(), [i], front(), back())が使えます。
まずは 3 行だけ
first_array.cpp C++ 最小例
#include <array>
std::array <int , 5 > a = {10 , 20 , 30 , 40 , 50 };
a[0 ] = 99 ; // vector と同じ感覚
std::cout << a.size (); // 5
ここまでで覚えること(2 つだけ):
std::array<型, サイズ> の形で作る(サイズはコンパイル時に決まる数字)
使い方は vector とほぼ同じ (a[i], a.size(), 範囲 for もそのまま)
よくある素朴な疑問
Q. vector があるのに、なぜ array が必要?
→ サイズが最初から決まっているなら、array の方が速い・小さい・安全 です。vector はヒープを使うので少しオーバーヘッドあり。また、array は C++17 の constexpr 文脈で使える(コンパイル時に計算できる)利点があります。
Q. int a[5] (C の配列)ではダメ?
→ 動きますが、関数に渡すとサイズを別引数で渡す必要があり 、境界チェックもない、= でコピーできない、など弱点が多い。std::array ならこれらすべて解決。
Q. サイズを変えたい場合は?
→ 変えられません。 サイズが変わる可能性があるなら最初から std::vector を使います。
2. C の生配列との違い
C の配列の何が不便かをまず確認します。
C の生配列 C
int a[5 ] = {1 ,2 ,3 ,4 ,5 };
// サイズを自分で計算
size_t n = sizeof (a) / sizeof (a[0 ]);
// 関数に渡すとポインタに減衰 → サイズ不明に
void f (int * arr) { /* サイズを別引数で */ }
f (a); // a は int* に
// = で代入できない
int b[5 ];
// b = a; ← エラー
std::array C++
std::array <int , 5 > a = {1 ,2 ,3 ,4 ,5 };
// サイズはメソッドで
auto n = a.size (); // 5
// 関数に渡しても型にサイズが含まれる
void f (const std::array <int , 5 >& a);
f (a);
// = でコピーできる
std::array <int , 5 > b = a;
ポインタ減衰(decay)とは?
C では関数に配列を渡すと、先頭要素へのポインタに変換 されます。これを「ポインタ減衰」と呼びます。例:
減衰の例 C
void f (int arr[5 ]) { // ← 書いても無視される
// arr は実は int* (サイズ情報なし)
size_t n = sizeof (arr) / sizeof (arr[0 ]);
// n は 1 or 2 になる(ポインタのサイズ÷intのサイズ)
}
int a[5 ];
f (a);
これが C の関数では「サイズを別引数で渡す」ことが定番な理由です。std::array は型の一部としてサイズを持つ ので減衰せず、関数内でも a.size() が正しく 5 を返します。
3. 基本操作(vector とほぼ同じ)
嬉しいことに、std::array の使い方はほぼ std::vector と同じです。新しく覚えることは「サイズを変える操作(push_back / erase / resize)が使えない 」だけ。
① アクセス
vector と同じ API 共通
std::array <int , 5 > a = {10 ,20 ,30 ,40 ,50 };
int x = a[1 ]; // 20
int y = a.at (1 ); // 20 (範囲チェックあり)
int f = a.front (); // 10
int b = a.back (); // 50
int * p = a.data (); // &a[0]
auto n = a.size (); // 5(常に 5)
② 範囲 for / イテレータ
vector と同じ 共通
for (auto x : a) std::cout << x << ' ' ;
for (auto & x : a) x *= 2 ; // 全要素 2 倍
// STL アルゴリズムも使える
std::sort (a.begin (), a.end ());
③ array 特有のメソッド
fill / swap array 独自
a.fill (0 ); // 全要素を 0 で埋める
std::array <int , 5 > b;
a.swap (b); // a と b の中身を高速交換
④ 使えない(= サイズが変わる)操作
NG な操作 array では無い
// これらは全部 array にはない
a.push_back (6 ); // ← コンパイルエラー
a.pop_back (); // ← エラー
a.erase (...); // ← エラー
a.resize (10 ); // ← エラー
a.clear (); // ← エラー
これらが必要なら最初から vector を選ぶ設計判断になります。
4. array vs vector vs 生配列 ― 比較表
項目 int a[N] (C 配列)std::array<T, N>std::vector<T>
サイズ決定 コンパイル時 コンパイル時 実行時(変更可)
メモリ スタック スタック ヒープ
size() メソッド なし あり あり
関数渡しで情報保持 減衰して喪失 保持 保持
= でコピー 不可 可 可
at() 範囲チェック なし あり あり
要素の追加・削除 不可 不可 可
constexpr で使える 可 可 C++20 で条件付き
速度・オーバーヘッド 最速 最速(配列と同等) わずかに遅い
使い分けの判断フロー:
サイズが変わる → std::vector
サイズ固定・コンパイル時に既知 → std::array
C 互換が必要/極小サイズの固定バッファ → C 配列(でも基本は array 推奨)
⭐
ここまでで std::array の基本は OK
残りは実用的な使い方。興味があれば。
5. よくある使い方
① 固定サイズの係数・テーブル
曜日名 定数テーブル
const std::array <std::string , 7 > weekdays = {
"Sun" , "Mon" , "Tue" , "Wed" ,
"Thu" , "Fri" , "Sat"
};
std::cout << weekdays[3 ]; // Wed
② 座標・ベクトル
3D 座標 数値計算
using Vec3 = std::array <double , 3 >;
Vec3 p = {1.0 , 2.0 , 3.0 };
Vec3 q = {4.0 , 5.0 , 6.0 };
// 内積を計算
double dot = 0 ;
for (size_t i = 0 ; i < 3 ; ++i) dot += p[i] * q[i];
③ 関数からの複数戻り値(pair/tuple の代替)
RGB を返す 同じ型 3 つ
std::array <int , 3 > get_rgb (int color) {
return {(color >> 16 ) & 0xFF ,
(color >> 8 ) & 0xFF ,
color & 0xFF };
}
auto [r, g, b] = get_rgb (0x00FF A0);
同じ型の値を 3 つ返すなら tuple より array のほうがシンプル。構造化束縛で分解できるのも同じ。
C++17 の小ネタ: C++17 以降、std::array a{1, 2, 3}; と書くと型とサイズが自動推論されます(std::array<int, 3> と同じ)。template argument deduction (CTAD) の機能。
確認クイズ
std::array の理解度を 4 問で確認。
Q1. std::array<int, 5> に push_back(6) を呼ぶとどうなる?
要素数が 6 になる
6 が末尾要素を置き換える
コンパイルエラー(array に push_back は無い)
実行時エラー
array はサイズ固定 なので、要素数を変える push_back, pop_back, resize, erase, clear はすべて存在しません。コンパイルエラーで弾かれます(安全)。サイズを変えたいなら vector に切り替え。
Q2. C の int a[5] と std::array<int, 5> の違いとして正しくない のは?
array は .size() メソッドを持つ
array は = で代入(コピー)できる
array は関数に渡してもサイズが保たれる
array はヒープに確保される
array はスタック に確保されます(vector はヒープ)。①②③はすべて array の利点として正しい。スタック確保なので高速で、ヒープを介さずに使えるのが array の強みです。
Q3. 次のうち、std::array を選ぶべきケースは?
ユーザー入力を読み込んで溜める(個数不明)
RGB(3 値)の色情報をまとめる
ログメッセージを時系列に蓄積する
CSV ファイルから読み取った行を保持する
array はサイズが最初から決まっていて変わらない ケースに最適。RGB の 3 値、曜日の 7 つ、座標の x/y/z など。個数が実行時に決まる/変わるものは vector を使います。
Q4. std::array<int, 4> a{1,2,3,4}; a.fill(0); の後、a の中身は?
{1, 2, 3, 4}(変化なし)
{}(空)
{0, 0, 0, 0}
{0, 2, 3, 4}
fill(v) はすべての要素を v で埋めます。サイズは変わりません(常に 4)。array 特有の便利メソッド。「リセット」でよく使われます。