C++で配列の長さを取得する方法は、どのような配列を使っているかによって変わります。
一口に「配列」といっても、C++には主に次のような種類があります。
- 生配列
- 関数に渡された生配列
std::arraystd::vector- 文字配列
std::string
それぞれ性質が異なるため、長さの取得方法も同じではありません。
特に注意したいのは、生配列は場所によってサイズ情報を保持できる場合と、失う場合があるという点です。
この違いを理解しておくと、配列の長さ取得でよくあるミスを防げます。
生配列の長さを取得する方法
固定長の生配列なら要素数を取得できる
固定長の生配列であれば、配列そのものが見えている場所では要素数を取得できます。
たとえば、要素が5個ある配列なら、配列全体のサイズを1要素分のサイズで割ることで、要素数を求められます。
この考え方自体は正しいです。
配列全体がメモリ上でどれだけの容量を使っているかを調べ、その容量を1要素あたりの容量で割れば、配列にいくつ要素があるかがわかります。
現代的なC++では std::size() が使いやすい
C++17以降であれば、生配列の長さを取得する方法として std::size() が使えます。
これは、生配列に対して要素数を取得できる便利な機能です。
従来のように「配列全体のサイズを1要素のサイズで割る」という書き方よりも、意味がわかりやすく、読み間違いも起きにくいです。
そのため、C++17以降の環境であれば、生配列の要素数取得には std::size() を使うのがおすすめです。
sizeof を使う方法も正しい
古いC++のコードでは、sizeof を使って配列の長さを求める書き方がよく使われます。
この方法も、配列そのものが見えている場所では正しく動作します。
ただし、sizeof が返すのは要素数ではなく、メモリ上のバイト数です。
そのため、配列全体のバイト数を1要素分のバイト数で割ることで、要素数を求めるという考え方になります。
sizeof の戻り値は std::size_t
細かい点ですが、sizeof の戻り値は整数型の一種である std::size_t です。
そのため、配列の長さを変数に入れる場合は、int よりも std::size_t を使うほうが自然です。
小さな配列であれば int に代入しても問題にならないことが多いですが、C++としては std::size_t を使うほうが正確です。
関数に渡された生配列では長さを取得できない
関数の引数では配列がポインタとして扱われる
C++で特に注意が必要なのは、関数に生配列を渡した場合です。
関数の引数に配列を書いた場合、見た目は配列を受け取っているように見えても、実際にはポインタとして扱われます。
つまり、関数の中では配列全体のサイズ情報が失われています。
この状態で sizeof を使っても、配列全体のサイズは取得できません。
取得できるのは、配列の先頭要素を指すポインタのサイズです。
関数内で sizeof を使うと誤った結果になる
関数に渡された生配列に対して、配列全体のサイズを1要素のサイズで割る方法を使うと、正しい要素数にはなりません。
たとえば、64bit環境ではポインタのサイズが8バイトになることが多く、要素型によっては計算結果が2や1のような値になる場合があります。
ただし、この結果は環境や要素の型によって変わります。
重要なのは、関数内では配列の本当の要素数はわからないということです。
関数に生配列を渡すなら長さも一緒に渡す
生配列を関数に渡す場合は、配列だけでなく要素数も一緒に渡すのが基本です。
配列の先頭位置だけを渡しても、関数側には「どこまでが配列なのか」という情報がありません。
そのため、呼び出し元で配列の長さを求め、その長さを別の引数として関数に渡す必要があります。
これはC言語由来の配列を扱うときの基本的な考え方です。
テンプレートを使えば関数内でも生配列の長さを扱える
配列を参照として受け取る方法がある
C++では、テンプレートを使って配列を参照として受け取ることで、関数内でも配列の長さを扱えます。
通常、関数の引数に生配列を渡すとポインタに変換されます。
しかし、配列への参照として受け取れば、配列の要素数情報を保持できます。
この方法を使うと、要素数をコンパイル時に取得できます。
型や要素数を汎用的に扱える
テンプレートを使えば、int の配列だけでなく、double や char など、さまざまな型の配列に対応できます。
また、要素数が異なる配列にも対応できます。
そのため、生配列を関数で安全に扱いたい場合には、テンプレートによる配列参照は有効な方法です。
ただし、初心者向けのコードでは、長さを別引数で渡すほうが理解しやすい場合もあります。
std::array の長さを取得する方法
固定長配列には std::array が使いやすい
C++で固定長の配列を扱う場合、生配列ではなく std::array を使う方法があります。
std::array は、標準ライブラリで用意されている固定長配列のコンテナです。
生配列と違って、オブジェクトとして扱えるため、メンバ関数を使って要素数を取得できます。
std::array は .size() で長さを取得できる
std::array では、.size() を使って要素数を取得できます。
生配列のように、配列全体のサイズと1要素のサイズを使って計算する必要はありません。
そのため、コードの意味がわかりやすくなります。
std::array はサイズも型の一部になる
std::array では、要素の型だけでなく、要素数も型の一部になります。
たとえば、要素数が3の std::array と、要素数が5の std::array は別の型として扱われます。
そのため、関数でさまざまな要素数の std::array を受け取りたい場合は、テンプレートを使う必要があります。
std::vector の長さを取得する方法
可変長の配列風データには std::vector が適している
要素数が実行時に変わる場合は、std::vector を使うのが一般的です。
std::vector は、要素を追加したり削除したりできる動的配列コンテナです。
厳密には生配列そのものではありませんが、連続したメモリ領域に要素を格納するため、配列のように扱えます。
std::vector も .size() で長さを取得できる
std::vector の要素数は .size() で取得できます。
要素を追加すれば .size() の結果は増え、要素を削除すれば減ります。
そのため、実行時に要素数が変わるデータを扱う場合は、生配列よりも std::vector を使うほうが安全で便利です。
標準C++では可変長配列は使えない
C++では、実行時に入力された値を使って生配列の長さを決めるような可変長配列は、標準機能としては使えません。
一部のコンパイラでは拡張機能として使える場合がありますが、標準C++としては移植性がありません。
そのため、実行時に長さが決まる配列を使いたい場合は、std::vector を使うのが基本です。
文字配列の長さには注意が必要
文字配列の要素数と文字列の長さは違う
文字配列では、「配列の要素数」と「文字列としての長さ」が異なる場合があります。
たとえば、C形式の文字列では、文字列の最後に終端文字が入ります。
この終端文字は、文字列の終わりを示すための特別な文字です。
そのため、見た目の文字数が5文字でも、配列としては終端文字を含めて6要素になることがあります。
文字列としての長さは std::strlen() で取得する
C形式文字列の文字数を知りたい場合は、配列の要素数ではなく、文字列としての長さを取得する必要があります。
このときに使われるのが std::strlen() です。
std::strlen() は、終端文字の手前までの文字数を数えます。
そのため、終端文字自体は文字列の長さには含まれません。
std::strlen() は終端文字が必要
std::strlen() を使う場合は、対象の文字配列が正しく終端文字で終わっている必要があります。
終端文字がない文字配列に対して std::strlen() を使うと、メモリ上のどこかで終端文字が見つかるまで読み続けてしまう可能性があります。
これは危険で、未定義動作につながることがあります。
安全に文字列を扱いたい場合は、C形式文字列よりも std::string を使うほうが一般的です。
std::string の長さを取得する方法
std::string は .size() で長さを取得できる
C++で文字列を扱う場合は、std::string を使うのが一般的です。
std::string では、.size() を使って長さを取得できます。
英数字だけの文字列であれば、.size() の結果は見た目の文字数と一致することが多いです。
日本語文字列ではバイト数になることがある
注意点として、std::string::size() が返す値は、必ずしも「人間が見た文字数」と一致するわけではありません。
特にUTF-8の日本語文字列では、1文字が複数バイトで表現されます。
そのため、日本語の文字列に対して .size() を使うと、文字数ではなくバイト数としての長さが返ることがあります。
たとえば、「こんにちは」は見た目では5文字ですが、UTF-8では15バイトになることがあります。
そのため、日本語の文字数を正確に数えたい場合は、文字エンコーディングを考慮した処理が必要です。
配列の長さとメモリサイズは別物
要素数とバイト数を区別する
C++で配列を扱うときは、「長さ」と「サイズ」という言葉を区別することが大切です。
配列の長さという場合、多くの場合は要素数を指します。
一方、sizeof で取得できるサイズは、メモリ上で使用しているバイト数です。
つまり、同じ「サイズ」という言葉でも、文脈によって意味が異なります。
sizeof は要素数ではなくバイト数を返す
sizeof は、対象のデータがメモリ上で何バイト使われているかを返します。
配列の要素数を直接返すわけではありません。
そのため、生配列の要素数を求めるには、配列全体のバイト数を1要素分のバイト数で割る必要があります。
この仕組みを理解しておくと、sizeof を使った配列長の取得方法がわかりやすくなります。
std::size() はポインタには使えない
生配列やコンテナには使える
std::size() は、生配列や、.size() を持つ標準コンテナに対して使えます。
たとえば、生配列、std::array、std::vector などには使用できます。
同じ書き方で要素数を取得できるため、C++17以降では便利な方法です。
単なるポインタには使えない
一方で、std::size() は単なるポインタには使えません。
これは重要な特徴です。
関数に渡された生配列はポインタとして扱われるため、その状態では std::size() を使って長さを取得することはできません。
ただし、これは安全性の面ではメリットでもあります。
sizeof を使った方法では、関数内で誤った値を出してしまう可能性があります。
一方、std::size() はポインタに対して使えないため、間違った長さを計算する前にコンパイルエラーとして気づけます。
C++で配列の長さを取得するときのおすすめの考え方
生配列なら std::size() を使う
C++17以降で、生配列そのものが見えている場所なら、std::size() を使うのがおすすめです。
従来の sizeof を使った方法よりも、意図がわかりやすく、安全です。
ただし、C++14以前の環境では std::size() が使えないため、その場合は sizeof を使った方法や、自作の補助関数を使うことになります。
関数に生配列を渡すなら長さも渡す
生配列を関数に渡す場合は、配列だけでは長さ情報が失われます。
そのため、要素数も別に渡す必要があります。
または、テンプレートで配列参照として受け取る方法を使えば、関数内でも長さを扱えます。
固定長なら std::array が便利
固定長の配列をC++らしく扱いたい場合は、std::array が便利です。
.size() で要素数を取得でき、標準コンテナとして扱えるため、生配列よりも安全で読みやすいコードになりやすいです。
可変長なら std::vector が基本
要素数が実行時に変わる場合は、std::vector を使うのが基本です。
.size() で現在の要素数を取得でき、要素の追加や削除もできます。
標準C++では可変長の生配列は使えないため、動的に長さが変わるデータには std::vector を選ぶのが自然です。
文字列なら std::string を使う
文字列を扱う場合は、C形式文字列よりも std::string を使うほうが安全です。
.size() で長さを取得できますが、日本語などのマルチバイト文字では、見た目の文字数ではなくバイト数になることがある点には注意が必要です。
まとめ
C++で配列の長さを取得する方法は、配列の種類によって異なります。
生配列であれば、配列そのものが見えている場所では std::size() や sizeof を使って要素数を取得できます。
ただし、関数に渡された生配列はポインタとして扱われるため、関数内で同じ方法を使っても正しい長さは取得できません。
この場合は、長さを別の引数として渡すか、テンプレートで配列参照として受け取る必要があります。
固定長の配列を安全に扱いたいなら std::array、要素数が変わる配列を扱いたいなら std::vector を使うのがおすすめです。
文字列の場合は、文字配列の要素数と文字列としての長さが異なることがあります。
特にC形式文字列では終端文字が含まれるため、配列の要素数と文字数は一致しない場合があります。
最終的には、次のように考えるとわかりやすいです。
- 生配列を使うなら、配列そのものが見えている場所で
std::size()を使う。 - 関数に渡すなら、長さも一緒に渡す。
- 固定長なら
std::array、可変長ならstd::vector、文字列ならstd::stringを使う。
このように使い分けることで、C++の配列の長さを安全かつ正確に取得できます。
以上、C++の配列の長さを取得する方法についてでした。
最後までお読みいただき、ありがとうございました。
