C言語の配列を簡単にコピーする方法

604, 2022-12-19

目次

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文を要素数分だけ回します。
↑の場合、カウント変数i0から3まで回ります。
このカウント変数iary1ary2にアクセスして要素をコピーします。

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関数を使った方がいいです。
memcpymemmoveの速度差はそれほどないのでそれなら安全な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プリンタ~



この記事のアンケートを送信する