C言語の配列を簡単にコピーする方法
目次
C言語の配列を簡単にコピーするには?
C言語で配列をコピーしたい場合、方法は全部で4つあります。
1つ目が「for文でコピーする方法」です。
これはfor文を使って地道に配列をコピーします。
2つ目が「memcpy関数でコピーする方法」です。
「string.h」に含まれるmemcpy関数を使うと配列をコピーできます。
3つ目が「memmove関数でコピーする方法」です。
これも「string.h」に含まれる関数で配列のコピーに使えます。
4つ目が「自作関数でコピーする方法」です。
自作関数を作ることでコピーのアルゴリズムを後から変更できます。
この記事ではこれら4つの方法について簡単に解説していきます。
for文でコピーする方法
最も原始的なコピー処理がfor文を使ったコピーです。
これが一番直感的かもしれません。
int ary1[] = {1, 2, 3, 4}; int ary2[4]; // for文で配列をコピーする for (int i = 0; i < 4; i++) { ary2[i] = ary1[i]; } // コピー結果を出力する for (int i = 0; i < 4; i++) { printf("%d\n", ary2[i]); }
出力結果。
1 2 3 4
↑の場合、コピー元の配列がary1
です。
ary1
には要素が4つ入っています。
これをary2
にコピーします。
// for文で配列をコピーする for (int i = 0; i < 4; i++) { ary2[i] = ary1[i]; }
for文を要素数分だけ回します。
↑の場合、カウント変数i
は0
から3
まで回ります。
このカウント変数i
でary1
とary2
にアクセスして要素をコピーします。
for文の条件式にi < 4
というマジックナンバーを使っていますが、これは改善することもできます。
↓のようなマクロ関数を用意してこれを使います。
#define numof(ary) (sizeof ary / sizeof ary[0])
このマクロは静的な配列の要素数を求めるマクロです。
「sizeof ary / sizeof ary[0]
」は「配列全体のサイズ / 要素1つ分のサイズ」という式です。
配列全体のサイズに要素1つ分のサイズを割ると配列の要素数が求まります。
このマクロを使うと先ほどのコピー処理は
int ary1[] = {1, 2, 3, 4}; int ary2[4]; // for文で配列をコピーする(numofマクロを使う) for (int i = 0; i < numof(ary1); i++) { ary2[i] = ary1[i]; } // コピー結果を出力する for (int i = 0; i < numof(ary2); i++) { printf("%d\n", ary2[i]); }
↑のように書き直すことができます。
for文からマジックナンバーが消えて良い感じですね。
ただしこのnumof()
マクロにも弱点はあります。
それはポインタには使えないという点です。
配列の顔をしたポインタにこのマクロを使うと正しい要素数が求まらないので注意が必要です。
memcpy関数でコピーする方法
「string.h
」に含まれている「memcpy
関数」を使うと配列をコピーできます。
int ary1[] = {1, 2, 3, 4}; int ary2[4]; // memcpyでary2にary1をコピーする memcpy(ary2, ary1, sizeof ary1); // コピー結果を出力する for (int i = 0; i < 4; i++) { printf("%d\n", ary2[i]); }
出力結果。
1 2 3 4
memcpy
関数は↓のような構造をしています。
void *memcpy(void *dest, const void *src, size_t n);
dest
にはコピー先の配列を指定します。
src
にはコピー元の配列を指定します。
n
にはコピーするバイト数を指定します。
返り値はdest
へのポインタです。
コピーするバイト数ですがこれは配列が静的あるいは自動的な配列だった場合は
sizeof ary1
で求めることができます。
よってコードは
// memcpyでary2にary1をコピーする memcpy(ary2, ary1, sizeof ary1);
という風になります。
memcpy
関数の注意点としてはコピー先とコピー元の配列が同じメモリであってはいけないという点です。
配列のメモリ領域が重なっている場合はmemmove
関数を使います。
memmove関数でコピーする方法
「string.h
」に含まれる「memmove
関数」を使っても配列をコピーできます。
int ary1[] = {1, 2, 3, 4}; int ary2[4]; // memmoveでary2にary1をコピーする memmove(ary2, ary1, sizeof ary1); // コピー結果を出力する for (int i = 0; i < 4; i++) { printf("%d\n", ary2[i]); }
出力結果。
1 2 3 4
memmove
関数は↓のような構造をしています。
void *memmove(void *dest, const void *src, size_t n);
dest
にはコピー先の配列を指定します。
src
にはコピー元の配列を指定します。
n
にはコピーするバイト数を指定します。
返り値はdest
へのポインタです。
memmove
関数とmemcpy
関数の大きな違いは、memmove
関数はコピー領域が重なっていてもちゃんと動作するという点です。
ですので
int ary[] = {1, 2, 3, 4}; memmove(ary, ary, sizeof ary); // コピー結果を出力する for (int i = 0; i < 4; i++) { printf("%d\n", ary[i]); }
というコードも合法になります。
memcpy
関数とmemmove
関数のどちらを使った方が良いのか?
という点ですが、これはmemmove
関数を使った方がいいです。
memcpy
とmemmove
の速度差はそれほどないのでそれなら安全なmemmove
を使った方が事故を減らすことができます。
関連記事
C言語の構造体をコピーする
自作関数を作りコピーする方法
配列をコピーするための自作関数を作っておくのも手です。
たとえば↓のような関数です。
/** * 配列をコピーするラッパー関数 */ void *copy_array(void *dst, const void *src, size_t len) { char *p = dst; const char *q = src; const char *end = src + len; for (; q < end; ) { *p++ = *q++; } return dst; }
このようなラッパーを1つ作っておけば、コピーするアルゴリズムを後から変更することができます。
↑の場合はfor文でコピー処理を実装していますが、これをmemcpy()
に変えることもできます。
関数呼び出しのコストが気になる場合はインライン関数にしても良いでしょう。
int ary1[] = {-1, -2, 3, 4}; int ary2[4]; // 自作関数でコピーする copy_array(ary2, ary1, sizeof ary1); // コピー結果を出力する for (int i = 0; i < 4; i++) { printf("%d\n", ary2[i]); }
出力結果。
-1 -2 3 4
おわりに
今回はC言語の配列のコピーについて解説しました。
配列のコピーはわりと必要になることがありますので押さえておいて損はないですね。
(^ _ ^) | 〇〇さん、これコピーお願い |
(・ v ・) | memmoveプリンタ~ |