11.

C++で配列の要素数を求める方法|sizeof・std::size・ポインタ減衰の注意

編集

C++で配列(1次元)の要素数を求めるには、組み込み配列なら sizeof(arr) / sizeof(arr[0]) または C++17 の std::size(arr) を使い、std::arraystd::vector なら .size() を呼びます。最も注意すべき点は、組み込み配列を関数の引数として渡すと配列がポインタに変わり(ポインタ減衰)、sizeof による要素数計算が正しく動かなくなることです。

この記事の要点
  • 組み込み配列の要素数は sizeof(arr) / sizeof(arr[0]) で求められる。
  • C++17 以降は std::size(arr) が安全で読みやすい(<iterator> をインクルード)。
  • std::arraystd::vector.size() メンバ関数で要素数を取得する。
  • 要素を順に処理したいだけなら範囲for for (auto x : arr) が簡潔。
  • 組み込み配列を関数に渡すとポインタに変わり、sizeof での要素数計算は機能しない(ポインタ減衰)。
  • 多次元配列の要素数は 多次元配列の要素数 を参照。

 

基本: sizeof を使う方法

組み込み配列の要素数は、配列全体のバイト数を 1 要素のバイト数で割ることで求められます。sizeof(arr) は配列全体のバイト数、sizeof(arr[0]) は先頭要素のバイト数を返すため、これらの商が要素数になります。この方法は古い C++(C++03 以前)でも使え、ヘッダのインクルードも不要です。

#include <iostream>

int main() {
    int arr[] = {10, 20, 30, 40, 50};

    std::size_t count = sizeof(arr) / sizeof(arr[0]);

    std::cout << "要素数: " << count << "\n"; // 5
}

 

sizeof(arr[0]) の代わりに sizeof(*arr)sizeof(arr) / sizeof(int) と書くこともできますが、要素の型を直接書くと型を変えたときに修正漏れが起きやすいため、sizeof(arr[0]) の形が安全です。

std::size を使う方法(C++17)

C++17 からは std::size が標準ライブラリに追加され、配列の要素数をより明確に取得できるようになりました。<iterator> をインクルードして使います(多くの実装では <array><vector> 経由でも利用できます)。sizeof の割り算より意図が読み取りやすく、コンテナにもそのまま使えるのが利点です。

#include <iostream>
#include <iterator>

int main() {
    int arr[] = {10, 20, 30, 40, 50};

    std::cout << std::size(arr) << "\n"; // 5
}

 

std::array / std::vector の場合

標準ライブラリのコンテナである std::arraystd::vector は、.size() メンバ関数で要素数を取得できます。これらは要素数を自身で管理しているため、関数に渡してもポインタに変わることがなく、sizeof のような落とし穴がありません。固定長なら std::array、可変長なら std::vector が選択肢になります。

#include <iostream>
#include <array>
#include <vector>

int main() {
    std::array<int, 5> a = {10, 20, 30, 40, 50};
    std::vector<int> v = {10, 20, 30};

    std::cout << a.size() << "\n"; // 5
    std::cout <<
v.size() << "\n"; // 3
}

 

範囲for で要素を順に処理する

要素数そのものではなく「全要素を順に処理したい」だけなら、要素数を数える必要はありません。C++11 以降の範囲for for (auto x : arr) を使えば、要素数を意識せずに配列の全要素をたどれます。組み込み配列・std::arraystd::vector のいずれにも使えます(ただし範囲forも、ポインタに減衰した配列には使えない点は sizeof と同じです)。

#include <iostream>

int main() {
    int arr[] = {10, 20, 30, 40, 50};

    for (auto x : arr) {
        std::cout << x << " ";
    }
    std::cout << "\n"; // 10 20 30 40 50
}

 

重要: 関数に配列を渡すと sizeof が効かない(ポインタ減衰)

組み込み配列を関数の引数として渡すと、配列は先頭要素へのポインタに変換されます。これをポインタ減衰(配列からポインタへの暗黙変換)と呼びます。減衰後はポインタのバイト数(多くの環境で 4 または 8 バイト)しか分からなくなるため、関数の中で sizeof(arr) / sizeof(arr[0]) を計算しても、元の要素数ではなく「ポインタのバイト数 ÷ 要素のバイト数」が求まってしまいます。

#include <iostream>

// arr は実際には int* に減衰している
void printCount(int arr[]) {
    // 期待は 5 だが、ポインタのバイト数で計算されてしまう
    std::cout << sizeof(arr) / sizeof(arr[0]) << "\n";
}

int main() {
    int arr[] = {10, 20, 30, 40, 50};
    std::cout << sizeof(arr) / sizeof(arr[0]) << "\n"; // 5(正しい)
    printCount(arr);   // 例: 2 など(誤り)
}

 

この問題を避けるには、要素数を別の引数として一緒に渡すか、ポインタに減衰しない std::array / std::vector を引数に使う、あるいは配列参照を受け取るテンプレートにするといった方法があります。要素数を扱う処理を関数に分ける場合は、組み込み配列をそのまま渡さない設計が安全です。

各手法の比較

手法 主な対象 C++バージョン 減衰後の動作 安全性
sizeof(arr)/sizeof(arr[0]) 組み込み配列 C++03〜 誤った値になる 条件付き
std::size(arr) 組み込み配列・コンテナ C++17〜 コンパイルエラーで気付ける 高い
.size() std::array / std::vector C++11〜 減衰しない 高い
範囲for 配列・コンテナ(走査のみ) C++11〜 減衰した配列には使えない 条件付き

 

std::size はポインタに対しては使えずコンパイルエラーになるため、減衰に気付かず誤った値を使ってしまう事故を防げる点で sizeof の割り算より安全です。要素数を確実に管理したい場面では、std::arraystd::vector の利用も検討してください。

多次元配列の場合

2 次元以上の配列では、各次元の要素数の求め方や注意点が 1 次元とは異なります。詳しくは関連記事 多次元配列の要素数 を参照してください。

落とし穴

落とし穴 内容
ポインタ減衰 関数に渡した組み込み配列はポインタになり、sizeof での要素数計算が機能しない。要素数を別途渡すかコンテナを使う。
sizeof はバイト数 sizeof(arr) は要素数ではなくバイト数を返す。要素数を得るには sizeof(arr[0]) で割る必要がある。
型を直接書いた割り算 sizeof(arr)/sizeof(int) のように要素の型を直書きすると、型変更時に修正漏れが起きやすい。
空配列・可変長 組み込み配列は要素数 0 を作れず、実行時に決まるサイズも扱えない。可変長が必要なら std::vector を使う。

 

FAQ

Q. sizeof(arr) / sizeof(arr[0])std::size(arr) はどちらを使うべきですか?
A. C++17 以降が使えるなら std::size(arr) をおすすめします。意図が明確で、ポインタに対してはコンパイルエラーになるため、ポインタ減衰による誤った要素数の使用を防げます。C++14 以前など std::size が使えない環境では sizeof の割り算を使います。

Q. なぜ関数の中だと要素数が正しく取れないのですか?
A. 組み込み配列を関数の引数として渡すと、配列が先頭要素へのポインタに変換される(ポインタ減衰する)ためです。関数内ではポインタのバイト数しか分からず、元の要素数は取得できません。要素数を一緒に引数で渡すか、std::array / std::vector を使ってください。

Q. std::vector の要素数が int と比較すると警告が出ます。
A. .size() は符号なし整数型(std::size_t)を返すため、int と比較すると符号の違いで警告が出ることがあります。受け取る変数を std::size_t にするか、用途に応じて適切にキャストして比較してください。

編集
Post Share
子ページ

子ページはありません

同階層のページ
  1. コメントアウト
  2. 文字列の結合/連結
  3. 変数の宣言
  4. 定数の宣言
  5. if文
  6. if文の論理演算子
  7. for文
  8. データ型(文字列以外)
  9. データ型(文字列)
  10. 配列とfor文
  11. 配列の要素数
  12. 多次元配列とfor文
  13. 多次元配列の要素数
  14. 関数の定義と呼び出し

最近更新/作成されたページ