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選でした。何かの役に立ったら幸いです。