ポインタを使って配列の要素を指すことはよくあります。ところが配列の範囲外であっても、ひとつだけ有効なアドレスが存在します。それが配列の最終要素のひとつ先を指すアドレスです。
C++プログラマーであれば、そういう最小要素のひとつ先には馴染みが深いのではないでしょうか? 標準ライブラリのアルゴリズム関数などでは、コンテナの先頭要素を指す反復子と、最終要素のひとつ先を指す反復子で範囲指定することが多いからです。
今回はそうした配列の最終要素のひとつ先のアドレスを求めてみます。
Cでは、元ネタにある次のようなマクロを使うのが普通です。
0 1 2 |
#define ArrayEnd(array) ((array) + ArraySizeOf(array)) |
ここでArraySizeOfは引数で指定した配列の要素数を求めるマクロです。
C++でもこうした配列はよく使われますし、C++03まではそのよう方法が主流だったと思います。一応、テンプレートを使ってこのように書くこともできましたが、あまり使われているのを見たことがありません。
0 1 2 3 4 5 6 |
template <typename T, std::size_t N> inline T* array_end(T (&array)[N]) { return array + N; } |
C++11以降では、std::end関数が導入されました。この関数はすぐれもので、引数で配列を渡せば配列の最終要素のひとつ先のアドレスを返し、std::vectorなどのコンテナを渡せばそのコンテナの最終要素のひとつ先の反復子を返してくれます。
0 1 2 3 4 5 6 |
int array[10]; auto last_of_array = std::end(array); // array + 10を返す。 std::vector<int> v(10); auto last_of_v = std::end(v); // v.end()を返す。 |
さらにC++14以降では、std::cend関数も使えるようになりました。std::cend関数を使えば、最終要素のひとつ先のconstポインタやconst反復子を取得することができます。
std::end関数やstd::cend関数があるということは、それらと対になるstd::begin関数やstd::cbegin関数ももちろんあります。これらの関数を使うことで、配列と列コンテナをほぼ同じシンタックスで扱えるようになっています。
なお、std::end関数やstd::cend関数(std::beginやstd::cbeginも)を使うときは<iterator>をインクルードするようにしてください。