【C言語】配列を0クリアで初期化する方法
- 作成日: 2021-12-27
- 更新日: 2024-03-12
- カテゴリ: C言語
C言語の配列を0クリアで初期化する
C言語の配列の各要素を0で初期化(0クリア)する方法について解説します。
C言語の配列はデフォルトで初期化される場合と初期化されない場合があります。
自動で初期化されない場合は手動で配列を初期化することが必要になります。
結論から言うと↓の方法があります。
- グローバル配列の初期化
- ローカル配列の初期化
- memsetを使った初期化
- for文を使った初期化
これらの配列の初期化方法について知っておくとC言語のプログラミングが捗るようになります。
それではグローバル配列の初期化から見ていきたいと思います。
C言語や他の言語を扱うYoutubeも公開しています。
興味がある方は以下のリンクからご覧ください。
グローバル配列の初期化
グローバル変数の配列はデフォルトで0クリアされます。
#include <stdio.h>
int ary[4]; // <- 0クリアされます
int main(void) {
printf("%d\n", ary[0]); // 0
printf("%d\n", ary[1]); // 0
printf("%d\n", ary[2]); // 0
printf("%d\n", ary[3]); // 0
return 0;
}
C言語では関数の外にあるグローバル変数と、関数の内側にあるローカル変数とでは扱いが変わります。
具体的にはグローバル変数は宣言と同時に0で初期化されますが、ローカル変数については宣言だけでは初期化されません。
ローカル変数の場合は定義が必要になります。
「0クリア、グッジョブ(GoodとGlobalを掛けてる)」と覚えておきましょう。
🦝 < グッジョブ
ローカル配列の初期化
関数などのブロックの内側にあるローカル変数の配列については宣言だけでは初期化されません。
↓のように初期化子リストで0クリアする必要があります。
#include <stdio.h>
int main(void) {
int ary[4] = {0}; // <- 0クリアされます
printf("%d\n", ary[0]); // 0
printf("%d\n", ary[1]); // 0
printf("%d\n", ary[2]); // 0
printf("%d\n", ary[3]); // 0
return 0;
}
上記のように波カッコ({}
)の中に0
を入れて、初期化子リストを作ります。
それを配列の宣言時に代入することで配列の要素全てが0クリアされます。
もちろん要素1つ1つに0を指定しても0クリアは可能です。
#include <stdio.h>
int main(void) {
int ary[4] = {0, 0, 0, 0};
printf("%d\n", ary[0]); // 0
printf("%d\n", ary[1]); // 0
printf("%d\n", ary[2]); // 0
printf("%d\n", ary[3]); // 0
return 0;
}
上記のように初期化子リストで各要素に値を設定する場合は、配列の要素数は省略できます。
#include <stdio.h>
int main(void) {
int ary[] = {0, 0, 0, 0}; // 要素数4の配列が定義される
printf("%d\n", ary[0]); // 0
printf("%d\n", ary[1]); // 0
printf("%d\n", ary[2]); // 0
printf("%d\n", ary[3]); // 0
return 0;
}
グローバル変数は0で初期化されますが、ローカル変数はこのように0で初期化されません。
ローカル変数に関しては開発者のタイミングで配列を初期化することができます。
初期化しなければその分処理は早くなるという理屈ですが、実際にはそこまでの差は出ません。
そのためローカル変数に関しては0で初期化するクセを付けておいた方が吉でしょう。
ちなみにこのような仕様は標準ライブラリのmalloc()
やcalloc()
関数にも見られます。
malloc()
はメモリ確保と同時に初期化はしませんが、calloc()
は確保したメモリを0クリアします。
特に速度が必要とされないプログラムではcalloc()
を使ったほうが事故は少なくなるでしょう。
memsetを使った初期化
string.h
をインクルードすると使えるmemset()
関数でも配列を0クリアできます。
#include <string.h>
/**
* sは初期化するポインタ
* cは初期化で要素に代入するデータ
* nはsのバイト数
* 返り値はsへのポインタ
*/
void *memset(void *s, int c, size_t n);
#include <string.h>
#include <stdio.h>
int main(void) {
int ary[4]; // <- 初期化されていない配列
memset(ary, 0, sizeof ary); // aryを0で初期化
printf("%d\n", ary[0]); // 0
printf("%d\n", ary[1]); // 0
printf("%d\n", ary[2]); // 0
printf("%d\n", ary[3]); // 0
return 0;
}
memset()
を使った初期化でありがちなバグが、memset()の第3引数の設定ミスです。
たとえば配列は配列でも配列のポインタからはsizeof
で配列のバイト数は求まりません。
void func(int *p) {
// sizeof pでは配列全体のバイト数は求まらない
memset(p, 0, sizeof p);
}
int main(void) {
int ary[4];
func(ary);
}
上記のようにするとsizeof p
では配列ではなくてポインタ変数のバイト数が求まります。
これはC言語でよくあるバグです。
C言語のsizeof
演算子はポインタと配列では異なる結果を返します。
memset()
はこの点に注意してください。
for文を使った初期化
原始的な方法ですがこれも配列の初期化になります。
for文を使って配列を初期化します。
#include <stdio.h>
int main(void) {
int ary[4]; // <- 初期化されていない配列
for (int i = 0; i < 4; i += 1) {
ary[i] = 0; // 要素を0で初期化
}
printf("%d\n", ary[0]); // 0
printf("%d\n", ary[1]); // 0
printf("%d\n", ary[2]); // 0
printf("%d\n", ary[3]); // 0
return 0;
}
for文を配列の要素だけ回して、その時のカウント変数を配列の添え字にします。
あとは配列のその添え字の要素を0で初期化すればOKです。
しかし上のようなコードはi < 4
と言う風にマジックナンバーを使っているのであまりメンテナンス性がよくありません。
配列の要素数を求められれば便利です。
そういう時は↓のようなマクロ関数を使います。
// 配列の要素数を求めるマクロ
#define numof(ary) (sizeof ary / sizeof ary[0])
このマクロ関数numof
は配列の要素数を求めます。
sizeof ary
で配列全体のサイズが求まり、sizeof ary[0]
で配列の要素1つ分のサイズが求まります。
配列全体のサイズを配列の要素1つ分のサイズで割ると、配列の要素数が求まります。
このマクロも例によってary
がポインタだった場合は期待した結果を返しません。
ここら辺はこのマクロの使用に注意が必要です。
このマクロ関数を使うと先ほどのコードは↓のように書き換えることができます。
#include <stdio.h>
// 配列の要素数を求めるマクロ
#define numof(ary) (sizeof ary / sizeof ary[0])
int main(void) {
int ary[4]; // <- 初期化されていない配列
for (int i = 0; i < numof(ary); i += 1) {
ary[i] = 0; // 要素を0で初期化
}
printf("%d\n", ary[0]); // 0
printf("%d\n", ary[1]); // 0
printf("%d\n", ary[2]); // 0
printf("%d\n", ary[3]); // 0
return 0;
}
関連記事
C言語のfor文の書き方【繰り返し文】
C言語のfor文とwhile文の使い分け方
おわりに
今回はC言語の配列を0で初期化する方法について解説しました。
用途に合わせて適切な初期化を行えばプログラミングが捗るかと思います。
🦝 < 0で初期化しちゃう
🐭 < 0クリア