for 文
最もオーソドックスな方法でしょう。
H * W
の二次元配列を、標準入力から得た値 x
で埋めます。
コード例
#include <stdio.h>
#define H 100
#define W 100
int main() {
int x;
scanf("%d", &x);
int A[H][W];
for (int i=0; i < H; ++i)
for (int j=0; j < W; ++j)
A[i][j] = x;
return 0;
}
memset
H * W
の二次元配列を -1
で埋めます。
コード例
#include <stdio.h>
#include <string.h>
#define H 100
#define W 100
int main() {
int A[H][W];
memset(A, -1, sizeof(A));
return 0;
}
解説
引数の意味は以下の通りです。
- 配列の先頭を指すポインタ
- 値(1byte)
- 配列の大きさ(byte数)
char txt[100];
memset(txt, 'A', sizeof(txt));
のように、文字列に対して使う例だと結果をイメージしやすいでしょうか。
memset の欠点
1
や 2
など、ほとんどの数は memset
で埋めるのに適していません。
memset(A, 1, sizeof(A))
というコード自体には問題ありませんが、配列 A
の中身は 1
ではなく 16843009
になってしまいます1。
その理由は、引数の解説で byte を強調したところからお察しかもですが、各値が 1byte 単位で埋められていくからです。
int
は 32bit == 4byte なので、1byte 単位で 1
(== 0x01
) を埋めていくと int
の値としては 16843009
(== 0x01010101
) になる、という動作だったのでした。
memset が有効な場合
代表例は 0
と -1
の 2 つです2。
0
で初期化するのであれば int A[H][W] = {};
3 のように、宣言と同時に初期化する方法も簡単です。
珍しい例では、memset(A, 0x3f, sizeof(A))
として「大きな値」で埋める使い方を競技プログラミングで見かけました。
0x3f
が選ばれた理由は、「 2 倍しても型の最大値を超えない、できるだけ大きい数」という競技プログラミング側の要求を満たすからでしょう。
fill_n (C++限定)
H * W
の二次元配列を、標準入力から得た値 x
で埋めます。
コード例
#include <iostream>
#include <algorithm>
using namespace std;
constexpr int H=100, W=100;
int main() {
int x;
cin >> x;
int A[H][W];
fill_n(&A[0][0], H*W, x);
}
解説
引数の意味は以下の通りです。
- 配列の先頭を指すポインタ
- 配列の大きさ(要素の個数)
- 値
memset
の第二引数と第三引数の役割が逆になったようなインターフェースですね。
memset
のコード例からも分かる通り、単に A
と書いても配列の先頭を指すポインタが得られます。
しかし、 fill_n
は賢く、ポインタ A
が持つ配列の大きさの情報を関数内部でも保持しているため、「*A
の型と x
の型が違う」と怒られてしまいます。
そのため、 &A[0][0]
のようにして int *
型のポインタを渡しています。
&A[0][0]
の他、(int *)A
のようにキャストしてもよいでしょう。
memset
ではできなかった 1
や 2
での埋めも fill_n
ならできます。すばらしいですね。
そもそも C++ なら vector
クラスを使えばよいという話はありますが、「動的メモリ確保は避けたい!」といったニーズで、あえて C の配列を使う場合には fill_n
の使いどころかもしれません。
以上、配列を特定の値で初期化する方法3選でした。何かの役に立ったら幸いです。