C言語の配列の要素数を得る方法
目次
- 配列の要素数を求めるには?
- 配列
- sizeof演算子
- 要素数を求める数式
- 数式を使って要素数を求めてみる
- マクロにしちゃう
- 注意事項
- サンプルコード
- 配列の要素数はどんな時に求めるか?
- 配列の要素数を求めるにはマクロを使おう
配列の要素数を求めるには?
今回はC言語の配列の要素数を得る方法を紹介します。
これはC言語を書いているとたまーに使うので、ぜひ覚えておいてください。
結論としては↓のマクロをよく使います。
#define numof(arr) (sizeof arr / sizeof arr[0])
このマクロは配列の要素数を求めるマクロです。
今回は具体的にこのマクロを解剖していきたいと思います。
関連記事
C言語の配列の使い方
C言語の動的配列のリサイズ方法
配列
まず、C言語における配列とは↓のようなものです。
int arr[] = {1, 2, 3};
これはint
型の配列arr
を定義しています。
この配列には要素1
, 2
, 3
が入っています。
この配列の要素数を求めたいケースはいろいろあります。
たとえばこの配列をfor
文で回したい時です。
for (int i = 0; i < len; i++) { printf("%d\n", arr[i]); }
この配列を回したい時、配列の要素数を求めることが出来ればfor
文をサクサク書くことができます。
こういった配列の要素数を求める方法は昔からよく知られている定石があります。
それが先ほど紹介したマクロです。
関連記事
C言語のfor文で配列を扱う方法
sizeof演算子
先ほどのマクロで使われているsizeof
演算子は値のバイト数を求めるのに使われます。
たとえばint
型やdouble
型の変数のバイト数を求めるには↓のようにします。
int a = 0; printf("%d\n", sizeof a); double b = 0.0; printf("%d\n", sizeof b);
↑の結果はたとえば↓のようになります。
4 8
これはint
型の変数のバイト数が4
バイト, double
型の変数のバイト数が8
バイトということです。
配列全体のバイト数を求める
このsizeof
演算子は配列にも使うことが出来ます。
配列に使った場合は、その配列全体のバイト数を得ることが出来ます。
int arr[] = {1, 2, 3}; printf("%d\n", sizeof arr);
↑の結果は↓のようになります。
12
int
型は変数1つが4
バイトでした。
配列arr
にはそれが3つ分、つまり3要素分あるので、全体のバイト数は4 * 3
で12バイトになるということです。
関連記事
C言語の配列の要素数を得る方法
配列の要素1つのバイト数を求める
配列の要素1つ分のバイト数を求めるには、添え字で最初の要素にアクセスして、その要素にsizeof
演算子をあてます。
int arr[] = {1, 2, 3}; printf("%d\n", sizeof arr[0]);
↑の結果は↓のようになります。
4
int
型の変数のバイト数は4
でしたが、int
型の配列の要素1つ分も同じバイト数ですね。
要素数を求める数式
ここまでで配列全体のバイト数と配列の要素1つ分のバイト数を得ることが出来ました。
あとは↓の数式で配列の要素数を求めることが出来ます。
配列の要素数 = 配列全体のバイト数 / 配列の要素1つ分のバイト数
割り算ですね。
たとえば配列全体が12
バイトだとします。そして要素1つ分のバイト数は4
です。
配列には要素(バイト数4
)が並んでいるわけですね。
その要素のバイト数を足していくと12
に達しますが、その時の要素数は3
つになるわけです。
{ 配列 [ 4バイト ] [ 4バイト ] [ 4バイト ] }
これは「12
バイトの中に4
バイトが3
つある」と言うのと同じです。
これを数式に表したのが先ほどの
配列の要素数 = 配列全体のバイト数 / 配列の要素1つ分のバイト数
になるわけですね。
数式を使って要素数を求めてみる
先ほどのsizeof
演算子と数式を使うと↓のように配列の要素数を求めることができます。
int arr[] = {1, 2, 3}; int len = sizeof arr / sizeof arr[0]; printf("len = %d\n", len);
↑の結果は↓のようになります。
len = 3
配列の要素数を求めることが出来てますね。
(^ _ ^) | やったぜ |
関連記事
C言語の配列の要素数を得る方法
マクロにしちゃう
先ほどのsizeof
演算子を使った式をいちいち書くのはめんどくさいのでマクロにしちゃいましょう。
#define numof(arr) (sizeof arr / sizeof arr[0])
このnumof
マクロを使うと↓のように書くことが出来ます。
int arr[] = {1, 2, 3}; int len = numof(arr); printf("len = %d\n", len);
結果は先ほどと同じです。
スッキリしてていいですね。
これはC言語で昔からよく知られているイディオムです。
注意事項
さて、便利なマクロですが注意事項があります。
このマクロはポインタ変数や関数の引数には使えません。
例えば配列のアドレスを代入してあるポインタ変数にこのマクロを使ってみると……
int arr[] = {1, 2, 3}; int *p = arr; printf("%d\n", numof(p));
結果は↓のようになります。
2
へんてこな数字になってますね。
これはsizeof p
で求められるバイト数が、ポインタ変数のバイト数だからです。
ポインタ変数には配列のアドレスが入ってますが、あくまでsizeof
演算子が求めるのはポインタ変数のバイト数です。
ポインタ変数のバイト数は私の環境では8
バイトになります。
そしてsizeof p[0]
で配列の要素のバイト数を求めているので、その結果は4
になります。
そして8
バイトを4
バイトで割っているので(8 / 4
)、結果は2
になるわけです。
これは関数の引数も同じです。
C言語では紛らわしいのですが、引数にも配列のように[]
を書くことが出来ます。
例えば↓のようにです。
void func(int arr[]) { }
しかし、この時の引数arr
は配列ではなくてただのポインタです。
なので、この引数に対してマクロを使っても、要素数を求めることは出来ません。
void func(int arr[]) { printf("%d\n", numof(arr)); } int main(void) { int arr[] = {1, 2, 3}; func(arr); return 0; }
↑の結果は↓のようになります。
2
サンプルコード
では最後にサンプルコードを掲載します。
numof
マクロを使ったコードです。
#include <stdio.h> #define numof(arr) (sizeof arr / sizeof arr[0]) int main(void) { int arr[] = {1, 2, 3, 4}; for (int i = 0; i < numof(arr); i++) { printf("%d\n", arr[i]); } return 0; }
↑のコードはnumof
マクロをfor
文で使っています。
配列をfor
文で回す場合、配列の長さを求めてその長さだけカウント変数をインクリメントさせる必要があります。
numof
マクロはこういう時にうってつけです。
↓のようにfor
文の比較部分でnumof
マクロを使うことで、コードをシンプルにすることができます。
for (int i = 0; i < numof(arr); i++) { ... }
比較してみましょう。↓のコードはnumof
マクロを使わないコードです。
どうでしょうか。numof
マクロを使ったほうがかっこいいですよね。
for (int i = 0; i < (sizeof arr / sizeof arr[0]); i++) { ... }
ちなみにこういった配列をループで回したいときは、↑のように単純に配列の長さを求める方法の他に、番兵を使った方法もあります。
番兵とは配列の末尾に設置する門番のことです。
この番兵がループしてる中で見つかったらループを終了させると、配列の先頭から末尾まで走査することができます。
#include <stdio.h> int main(void) { int arr[] = {1, 2, 3, 4, -1}; for (int i = 0; arr[i] != -1; i++) { printf("%d\n", arr[i]); } return 0; }
↑のコードは-1
の値を番兵に使っているケースです。
番兵を使えば配列の長さを求めなくてもループで配列を回すことが可能です。
この番兵を多用してるのが文字列です。
文字列にはナル文字(\0
)という文字列末尾に配置する番兵があります。
文字列の走査ではこの番兵であるナル文字を参照するのがC言語では一般的な方法です。
たとえばstrlen()
関数は番兵を使って文字列の長さを求めています。
そういう意味でstrlen()
は配列の長さを求める処理に似ている関数と言えます。
それからポインタの配列では一般的にNULL
ポインタを番兵に使います。
ポインタの配列はC言語ではわりと多用するデータ構造です。
配列の要素数はどんな時に求めるか?
配列の要素数を求めるケース。
これは↓が考えられます。
配列をfor文で回したい時
配列の長さを取得したい時
配列をfor文で回すには配列の要素数が必要です。
ですので今回使ったnumof()
マクロなどを使います。
また配列の要素にアクセスしたい時に配列の長さを使う場合もあります。
たとえば配列の一番末尾の要素を取り出す場合は
int len = numof(ary); int last = ary[len - 1];
というようなコードを書くこともあります。
もっとも↑のコードは悪い見本です。
なぜかというとlen
が0
のとき範囲外アクセスになるからです。。
配列の末尾のような配列を後ろから参照したいケースというのは意外と多いものです。
ですのでそういう時に配列の長さを求める必要があります。
自作コンテナ(自作ライブラリ)などを作れば配列と配列の長さをセットで扱って配列の長さをいちいち計算する手間を減らすということも可能です。
興味ある方は自作コンテナにチャレンジしてみてください。
配列の要素数を求めるにはマクロを使おう
まとめです。
配列の要素数を求めるには↓のようにしましょう。
sizeof演算子で配列全体のバイト数を求める
sizeof演算子で配列の要素1つ分のバイト数を求める
配列全体のバイト数を要素1つ分のバイト数で割る
マクロを使う
今回紹介したマクロはこれです。
#define numof(arr) (sizeof arr / sizeof arr[0])
配列の要素数を求めることが出来れば、色々なシーンで使えると思います。
ぜひこのマクロをあなたのメモ帳に追加してみてください。