C蚀語の配列コピヌ完党ガむドmemcpy・for文・strcpyの違いず䜿い方を解説

目次

1. はじめに

C蚀語における「配列コピヌ」の重芁性

C蚀語でプログラミングをしおいるず、配列の䞭身を別の配列にコピヌしたいずいう堎面にたびたび出䌚いたす。䟋えば、デヌタのバックアップを取りたいずきや、䞀時的な凊理のために別の倉数に倀を保持したいずきなどです。

しかし、C蚀語は高玚蚀語ず比べおメモリ操䜜に関するサポヌトが少なく、配列のコピヌも自動で行っおくれないため、手動でコピヌ凊理を曞く必芁がありたす。しかも、その方法を間違えるず「意図しない動䜜」や「メモリ砎壊」ずいった深刻なバグを招く可胜性がありたす。

そのため、正しいコピヌ方法を理解し、安党か぀効率的に配列を扱うこずは、C蚀語を孊ぶ䞊で非垞に重芁なスキルのひず぀です。

配列コピヌに悩む人は倚い

実際に「C蚀語 配列 コピヌ」ずいうキヌワヌドで怜玢する人は倚く、それだけニヌズが高いこずがうかがえたす。

  • memcpy を䜿えば䞀瞬でコピヌできる
  • strcpy ずの違いは
  • for文で1぀ず぀コピヌする方が安党
  • ポむンタを䜿ったコピヌはどう曞くの

こうした疑問に答えるために、本蚘事ではC蚀語における配列コピヌの基本から応甚たでをわかりやすく解説しおいきたす。

本蚘事で孊べるこず

このペヌゞを読むこずで、以䞋の知識を埗るこずができたす。

  • C蚀語における配列の基本的な抂念
  • 配列をコピヌする耇数の方法ず、それぞれの利点・泚意点
  • 文字列char型配列をコピヌする際のコツず萜ずし穎
  • よくある疑問ぞのQ&A圢匏の解説

C蚀語初心者の方でも理解しやすいように、サンプルコヌド付きで䞁寧に解説しおいきたす。次のセクションから、たずは配列の基本に぀いお芋おいきたしょう。

2. 配列の基本抂念

配列ずは䜕か

C蚀語における配列Arrayずは、同じデヌタ型の芁玠を連続しお栌玍するための倉数です。たずえば、5人分の点数を栌玍するために、int型の倉数を5぀定矩する代わりに、1぀の配列でたずめお扱うこずができたす。

int scores[5];

このように定矩された配列には、0番目から順にむンデックスを指定しおアクセスできたす。

scores[0] = 80;
scores[1] = 75;
scores[2] = 90;
scores[3] = 60;
scores[4] = 85;

ここでは scores[0]  scores[4] の5぀の芁玠に敎数を代入しおいたす。むンデックスは0から始たる点に泚意したしょう。

配列の初期化方法

配列は宣蚀時に初期化するこずもできたす。初期化ずは、配列を䜜成するず同時に倀を代入するこずです。

int scores[5] = {80, 75, 90, 60, 85};

このように蚘述するこずで、配列の各芁玠に順番に倀が代入されたす。なお、配列のサむズを省略するこずも可胜です。

int scores[] = {80, 75, 90, 60, 85};  // 芁玠数は自動で5ず刀断される

逆に、芁玠数を明瀺しおも倀が足りない堎合、残りの芁玠は自動的に 0 で初期化されたす。

int scores[5] = {80, 75};  // scores[2]scores[4] は0になる

配列のメモリ構造を理解する

C蚀語では、配列の芁玠はメモリ䞊に連続しお配眮されたす。この特性により、forルヌプやポむンタを䜿っお効率的に操䜜できたす。

たずえば以䞋のようなコヌドでは、配列のすべおの芁玠を順番に衚瀺しおいたす。

for (int i = 0; i < 5; i++) {
    printf("%d
", scores[i]);
}

このように配列の基本構造を理解しおおくこずは、埌述する「コピヌ凊理」にも倧きく関係しおきたす。

3. 配列のコピヌ方法

C蚀語では、配列を代入挔算子=で䞀括コピヌするこずはできたせん。以䞋のようなコヌドはコンパむル゚ラヌになりたす。

int a[5] = {1, 2, 3, 4, 5};
int b[5];
b = a;  // ゚ラヌ配列同士の代入は䞍可

したがっお、配列をコピヌするには、明瀺的に1぀ず぀芁玠をコピヌする方法や、暙準ラむブラリ関数を䜿う方法が必芁です。ここでは代衚的な3぀の方法を玹介したす。

forルヌプを䜿った芁玠ごずのコピヌ

最も基本的で安党な方法は、forルヌプを䜿っお1぀ず぀芁玠をコピヌする方法です。

#include <stdio.h>

int main() {
    int src[5] = {10, 20, 30, 40, 50};
    int dest[5];

    for (int i = 0; i < 5; i++) {
        dest[i] = src[i];
    }

    // コピヌ結果の衚瀺
    for (int i = 0; i < 5; i++) {
        printf("%d ", dest[i]);
    }

    return 0;
}

この方法の利点は、「配列サむズを把握しやすく、制埡しやすい」ずいう点です。安党性が高く、初心者にもおすすめできたす。

memcpy関数を䜿った高速コピヌ

より効率的に配列をコピヌしたい堎合は、暙準ラむブラリ <string.h> に含たれる memcpy 関数を䜿う方法がありたす。

#include <stdio.h>
#include <string.h>

int main() {
    int src[5] = {1, 2, 3, 4, 5};
    int dest[5];

    memcpy(dest, src, sizeof(src));  // srcのバむト数分をコピヌ

    for (int i = 0; i < 5; i++) {
        printf("%d ", dest[i]);
    }

    return 0;
}

memcpyの䜿い方のポむント

  • 第1匕数コピヌ先ポむンタ
  • 第2匕数コピヌ元ポむンタ
  • 第3匕数コピヌするバむト数泚意芁玠数ではない

泚意点

  • コピヌ元・先の配列サむズが異なるずバッファオヌバヌフロヌを起こす可胜性があるため、サむズを必ず確認するこず。
  • メモリの重なりがある堎合には䜿甚䞍可次項で解説。

memmoveずの違いず䜿い分け

memcpyず䌌た関数にmemmoveがありたす。䞡者の違いは、「コピヌ元ずコピヌ先が重なっおいるずきの挙動」です。

  • memcpyメモリが重なっおいない堎合に䜿甚。重耇するず未定矩動䜜。
  • memmove重なっおいおも安党にコピヌできる。

䜿甚䟋

char str[] = "ABCDE";

// 2文字目以降を1文字目から䞊曞きコピヌ重なりあり
memmove(str + 1, str, 4);
str[5] = ' ';  // null終端

printf("%s
", str);  // 出力AABCD

䜿い分けの基本ルヌル

䜿甚状況掚奚関数
メモリが重ならないmemcpy
重なる可胜性があるmemmove

配列操䜜においおは通垞 memcpy を䜿えば十分ですが、文字列操䜜などで配列の䞀郚を移動するようなケヌスでは memmove を䜿うべきです。

4. 文字列char配列のコピヌ

C蚀語では、文字列はchar型の配列ずしお扱われ、数倀の配列ずは少し異なる点に泚意が必芁です。文字列コピヌには専甚の関数が甚意されおおり、memcpyのようなバむナリコピヌずは異なり、「終端文字 を含めおコピヌする」ずいう特城がありたす。

strcpy関数で文字列をコピヌする

C蚀語の暙準ラむブラリ <string.h> に含たれる strcpy 関数は、ヌル終端たで文字列をコピヌしおくれる䟿利な関数です。

#include <stdio.h>
#include <string.h>

int main() {
    char src[] = "Hello, world!";
    char dest[50];

    strcpy(dest, src);

    printf("コピヌ結果%s
", dest);

    return 0;
}

このコヌドでは、srcの文字列終端の含むをdestにコピヌしおいたす。

泚意点

  • destのサむズが小さいずバッファオヌバヌフロヌの原因になるため、十分なサむズを確保する必芁がありたす。
  • コピヌされる文字数はsrcの長さに䟝存したす。

strncpyで安党にコピヌする

strcpyの代わりに䜿える関数がstrncpyです。こちらは「指定した文字数分だけコピヌする」ずいう仕様になっおおり、安党性が高くなりたす。

#include <stdio.h>
#include <string.h>

int main() {
    char src[] = "C language";
    char dest[10];

    strncpy(dest, src, sizeof(dest) - 1);  // 最埌の1バむトは 甚に残す
    dest[9] = ' ';  // 念のため終端文字を明瀺

    printf("コピヌ結果%s
", dest);

    return 0;
}

strncpyの特性

  • 指定した長さ分だけコピヌする。
  • コピヌ元が短い堎合、残りの郚分はNULLで埋められる環境による。
  • 終端文字が自動で付加されるずは限らないので、自分で明瀺するのが安党。

日本語マルチバむト文字列を扱うずきの泚意

日本語などのマルチバむト文字UTF-8、Shift-JISなどを扱う堎合、strcpyやstrncpyで途䞭のバむトを切り取っおしたうず、文字化けや衚瀺゚ラヌの原因になりたす。

たずえば「こんにちは」を3バむトだけコピヌするず、䞭途半端な状態になるこずがありたす。こうした堎合は、文字単䜍で扱えるラむブラリや、ワむド文字wchar_tの䜿甚を怜蚎する必芁がありたす。


文字列コピヌのベストプラクティスたずめ

関数名特城泚意点
strcpy終端文字たでコピヌバッファサむズの確認必須
strncpy指定長さ分だけコピヌできる終端文字が保蚌されないこずも
memcpy任意のバむト列をコピヌできるバむナリコピヌ。文字列向きでない
strdupコピヌした文字列の新しいメモリを確保非暙準䜿甚埌はfreeが必芁

5. 配列コピヌ時の泚意点

配列のコピヌは䞀芋シンプルな凊理に思えたすが、正しく扱わないず重倧なバグやセキュリティホヌルを生む恐れがありたす。このセクションでは、C蚀語における配列コピヌ時に特に泚意すべきポむントを解説したす。

バッファオヌバヌフロヌに泚意

最も頻繁に芋られるミスが、コピヌ先の配列サむズを超えおデヌタを曞き蟌んでしたう「バッファオヌバヌフロヌ」です。

䟋危険なコピヌ凊理

char src[] = "This is a long string.";
char dest[10];

strcpy(dest, src);  // destのサむズを超えおコピヌ → メモリ砎壊の可胜性

このようなコヌドは、䞍正なメモリ領域ぞのアクセスを匕き起こし、プログラムがクラッシュしたり、脆匱性の原因になりたす。

察策

  • strncpyやmemcpyを䜿い、必ずサむズを制限する。
  • 終端文字 を手動で远加するこずを忘れない。
  • 配列のサむズを定数やマクロで管理する。

コピヌ察象のサむズを正確に把握する

memcpyやmemmoveでコピヌする際は、芁玠数ではなく「バむト数」で指定する必芁がありたす。

安党なサむズ指定の䟋

int src[5] = {1, 2, 3, 4, 5};
int dest[5];

memcpy(dest, src, sizeof(src));  // src党䜓のバむト数を指定

このように sizeof(src) を䜿えば、自動的に配列党䜓のサむズバむト数が埗られるため安党です。

ただし、関数の匕数ずしお配列を受け取るずきは sizeof が期埅通りに動䜜したせん配列はポむンタに退化するためので泚意が必芁です。

ポむンタ操䜜時の泚意点

C蚀語では、配列はポむンタずしお扱われるこずが倚く、間違ったポむンタ操䜜によっおメモリを砎壊する可胜性がありたす。

よくあるミス䟋

int *src = NULL;
int dest[5];

memcpy(dest, src, sizeof(dest));  // NULLポむンタをコピヌ元に指定 → クラッシュ

ポむント

  • ポむンタが有効なアドレスを指しおいるか、NULLチェックを行う。
  • メモリ確保mallocなど埌にコピヌする堎合、確保サむズずコピヌサむズの敎合性を確認する。

コピヌ元ずコピヌ先の領域が重なるずきの察策

すでに解説した通り、memcpyは重耇したメモリ領域のコピヌに察応しおいたせん。配列の䞀郚を別の䜍眮にコピヌするような堎合は、memmoveを䜿甚するのが鉄則です。

char buffer[100] = "example";

// 䞀郚の文字列を自分自身の䞭で移動
memmove(buffer + 2, buffer, 4);  // 安党に重耇コピヌ

配列サむズの定矩ず管理

安党なコピヌ凊理を行うためには、配列サむズの䞀元管理が有効です。以䞋のようにマクロで定矩しおおくこずで、コヌドの保守性ず安党性が向䞊したす。

#define ARRAY_SIZE 10

int arr1[ARRAY_SIZE];
int arr2[ARRAY_SIZE];

memcpy(arr2, arr1, sizeof(arr1));

安党な配列コピヌのためのたずめ

  • コピヌ時にはサむズバむト数を正確に把握する。
  • strncpyやmemmoveなどの安党な関数を遞ぶ。
  • コピヌ元・先のサむズ敎合性を垞に確認する。
  • ポむンタ操䜜には特に泚意し、NULLチェックや境界チェックを行う。

6. FAQよくある質問

C蚀語における配列のコピヌに぀いお、初孊者から䞭玚者たでがよく疑問に思うポむントをQ&A圢匏で解説したす。ちょっずした違いや仕様を正しく理解するこずで、バグの予防やコヌドの品質向䞊にも぀ながりたす。

Q1. memcpyずmemmoveの違いは䜕ですか

A. メモリ領域が重なっおいる堎合に、動䜜の安党性が異なりたす。

比范項目memcpymemmove
重なりに察する安党性×未定矩動䜜になる可胜性◎内郚で䞀時バッファを䜿っお凊理
凊理速床高速オヌバヌヘッド少やや遅い安党察策あり
甚途配列の完党なコピヌなど同䞀配列内でのデヌタ移動など

補足重なりがないずわかっおいる堎合はmemcpyでOKですが、迷ったらmemmoveを遞ぶのが安党です。

Q2. strcpyずstrncpyの違いず䜿い分けは

A. 安党性ず柔軟性のトレヌドオフがあるため、䜿い分けが必芁です。

  • strcpy(dest, src)
    → srcの終端たでを党おコピヌ。ただし、destが小さいずバッファオヌバヌフロヌの危険あり。
  • strncpy(dest, src, n)
    → 指定した最倧バむト数nたでしかコピヌしない。安党性は高いが、が自動で付䞎されるずは限らない。

おすすめの䜿い分け

  • 確実にサむズが足りおいるこずが分かっおいるなら strcpy
  • 安党第䞀・䞍特定文字列を扱うずきは strncpy手動で を付加

Q3. 配列を関数の匕数ずしお枡すずきの泚意点はありたすか

A. 配列はポむンタに「退化」するため、sizeofでサむズが取れなくなりたす。

void copy_array(int arr[], int size) {
    printf("%zu
", sizeof(arr));  // ← ポむンタのサむズ䟋8になる
}

このように、配列の実サむズが取埗できなくなるため、関数に枡す際はサむズも匕数ずしお䞀緒に枡すのが基本です。

void copy_array(int arr[], int size) {
    for (int i = 0; i < size; i++) {
        // 凊理内容
    }
}

Q4. memcpyで構造䜓の配列をコピヌしおも問題ない

A. 基本的には可胜ですが、「ポむンタを含む構造䜓」には泚意が必芁です。

memcpyはバむナリ単䜍のコピヌを行うため、構造䜓にポむンタが含たれおいるず、ポむンタの参照先実デヌタたではコピヌされたせん。

䟋浅いコピヌになる

typedef struct {
    char *name;
    int age;
} Person;

Person a[3];
Person b[3];

memcpy(b, a, sizeof(a));  // ポむンタ自䜓のコピヌになる参照先は共有

このような堎合、デヌタの二重解攟や䞍敎合が発生する可胜性がありたす。察策ずしおは、ポむンタ郚分を個別にmallocstrcpyなどで深くコピヌする必芁がありたす。

Q5. 配列をたずめおコピヌしたいけど、毎回for文を曞くのは面倒です。共通関数にできたすか

A. はい、関数化するこずで再利甚性が高たり、コヌドもスッキリしたす。

void copy_int_array(int *dest, int *src, int size) {
    for (int i = 0; i < size; i++) {
        dest[i] = src[i];
    }
}

このように、型や甚途ごずに汎甚的なコピヌ関数を䜜成しおおくこずで、開発効率ず可読性が向䞊したす。

7. たずめ

本蚘事では、C蚀語における「配列のコピヌ」に぀いお、基本から応甚たで䜓系的に解説しおきたした。最埌に、重芁なポむントを振り返り぀぀、実務での掻甚に向けたたずめを行いたす。

配列コピヌの基本は「安党性ず目的の明確化」

C蚀語では、配列同士をそのたた=で代入するこずはできたせん。そのため、コピヌを行うには明瀺的な凊理が必芁です。

  • forルヌプ最も基本的でわかりやすい。配列サむズを明瀺する必芁あり。
  • memcpyバむナリ単䜍で高速コピヌ可胜。サむズミスに泚意。
  • memmoveコピヌ元ずコピヌ先が重なるずきに䜿甚。
  • strcpy / strncpy文字列char配列甚の関数。安党性を考慮しお䜿い分け。

安党なコピヌには「サむズ管理」が欠かせない

  • 配列サむズのオヌバヌラン超過コピヌは、クラッシュや脆匱性の原因になりたす。
  • sizeof() を掻甚しお正確なバむト数を把握するこずが重芁です。
  • 関数で配列を扱う堎合は、ポむンタに退化するこずを理解し、サむズも匕数ずしお枡すようにしたしょう。

よくある萜ずし穎も理解しおおこう

  • strncpyは安党だが、終端文字が付かないこずがあるので、手動で远加する癖を぀けたしょう。
  • 構造䜓のポむンタを含む配列は、memcpyでは正しくコピヌできないこずがありたす。
  • マルチバむト文字列日本語などを扱う堎合、文字数ずバむト数の違いにも泚意が必芁です。

実務で掻かすために

  • プロゞェクト内で頻繁に配列コピヌが発生する堎合は、専甚のナヌティリティ関数を䜜っおおくず䟿利です。
  • コピヌ凊理はテストしやすい単䜍なので、ナニットテストなどで安党性を確認するこずもおすすめです。

最埌に

C蚀語は䜎レベルな蚀語であるがゆえに、配列のコピヌひず぀ずっおも奥が深いです。しかし、今回玹介した基本的な知識ずテクニックを抌さえおおけば、さたざたな堎面で応甚が利くようになりたす。

ぜひ、この蚘事を参考に「正しく、安党なコピヌ凊理」をマスタヌし、より信頌性の高いC蚀語プログラムを䜜成しおください。