こんにちは、もがちゃんです。
今回は、C言語で値(メモリ領域)のコピーをする際に使用される memcpy と memmove の違いと使い方を簡単なサンプルプログラムとともに説明します。
目次
構文
memcpyの構文
#include <string.h>
void *memcpy(void * restrict s1,
const void * restrict s2, size_t n);
ポインタだけに指定できる修飾子で、ポインタが指す領域は重複が無いものとしてコンパイラが最適化しても良いことを指示するための修飾子
memmoveの構文
#include <string.h>
void *memmove(void *s1, const void *s2, size_t n);
説明
memcpyの説明
memcpyは、s2で指定されたポインタが指すメモリ領域から、s1で指定されたポインタが指すメモリ領域へ、nで指定されたバイト数分コピーします。
s2が指すメモリ領域とs1が指すメモリ領域が重なり合う場合の動作は保証されません。
memcpy関数は、s2が指すオブジェクトから、s1が指すオブジェクトに、n文字をコピーする。領域の重なり合うオブジェクト間でコピーが行われるとき、その動作は未定義とする。
JISX3010 プログラム言語C
コピー先メモリ領域の先頭ポインタを指定します。
コピー元メモリ領域の先頭ポインタを指定します。
コピー元からコピー先へコピーするバイトサイズを指定します。
s1を返します。
memmoveの説明
memmoveは、s2で指定されたポインタが指すメモリ領域から、s1で指定されたポインタが指すメモリ領域へ、nで指定されたバイト数分コピーします。
s2が指すメモリ領域とs1が指すメモリ領域が重なり合う場合も正しくコピーされます。
コピー先メモリ領域の先頭ポインタを指定します。
コピー元メモリ領域の先頭ポインタを指定します。
コピー元からコピー先へコピーするバイトサイズを指定します。
s1を返します。
memcpyとmemmoveの違い
memcpyとmemmoveの違いは、ポインタが指すメモリ領域に重複があった場合の動作が異なります。
memcpyの方は、メモリ領域に重複があった場合の動作は保証されず、memmoveの方は、メモリ領域に重複があった場合でも動作は保証されます。
従って、重複が無い場合は、重複が無い場合は、どちらでも同じ結果となります。
使い方サンプル
memcpyとmemmoveを使用してメモリ領域をコピーするサンプルプログラムを紹介します。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void) {
char buffer1[10]; /* 重複しない領域用1 */
char buffer2[10]; /* 重複しない領域用2 */
char buffer3[20]; /* 重複する領域用 */
/* 重複しない領域へのcopy */
char * restrict s1 = buffer1;
char * restrict s2 = buffer2;
memset(s2, '2', sizeof(buffer2));
/* s2 -> s1へ5バイト分コピー */
memset(s1, '1', sizeof(buffer1));
fprintf(stdout, "before memcpy s1[%.10s]\n", s1);
fprintf(stdout, "before memcpy s2[%.10s]\n", s2);
fprintf(stdout, ">after memcpy s1[%.10s]\n", (char*)memcpy(s1, s2, 5));
memset(s1, '1', sizeof(buffer1));
fprintf(stdout, "before memmove s1[%.10s]\n", s1);
fprintf(stdout, "before memmove s2[%.10s]\n", s2);
fprintf(stdout, ">after memmove s1[%.10s]\n", (char*)memmove(s1, s2, 5));
/* 重複する領域へのcopy その1 */
s1 = &buffer3[0];
s2 = &buffer3[1];
fprintf(stdout, "\n\n");
/* s2 -> s1へ15バイト分コピー */
memset(&buffer3[0], '1', 10);
memset(&buffer3[10], '2', 10);
fprintf(stdout, "before memcpy buffer3[%.20s]\n", buffer3);
fprintf(stdout, "before memcpy s1[%.15s]\n", s1);
fprintf(stdout, "before memcpy s2[%.15s]\n", s2);
fprintf(stdout, ">after memcpy s1[%.15s]\n", (char*)memcpy(s1, s2, 15));
memset(&buffer3[0], '1', 10);
memset(&buffer3[10], '2', 10);
fprintf(stdout, "before memmove buffer3[%.20s]\n", buffer3);
fprintf(stdout, "before memmove s1[%.15s]\n", s1);
fprintf(stdout, "before memmove s2[%.15s]\n", s2);
fprintf(stdout, ">after memmove s1[%.15s]\n", (char*)memmove(s1, s2, 15));
/* 重複する領域へのcopy その2 */
s1 = &buffer3[1];
s2 = &buffer3[0];
fprintf(stdout, "\n\n");
/* s2 -> s1へ15バイト分コピー */
memset(&buffer3[0], '1', 10);
memset(&buffer3[10], '2', 10);
fprintf(stdout, "before memcpy buffer3[%.20s]\n", buffer3);
fprintf(stdout, "before memcpy s1[%.15s]\n", s1);
fprintf(stdout, "before memcpy s2[%.15s]\n", s2);
fprintf(stdout, ">after memcpy s1[%.15s]\n", (char*)memcpy(s1, s2, 15));
memset(&buffer3[0], '1', 10);
memset(&buffer3[10], '2', 10);
fprintf(stdout, "before memmove buffer3[%.20s]\n", buffer3);
fprintf(stdout, "before memmove s1[%.15s]\n", s1);
fprintf(stdout, "before memmove s2[%.15s]\n", s2);
fprintf(stdout, ">after memmove s1[%.15s]\n", (char*)memmove(s1, s2, 15));
}

メモリ領域が重複しない場合と重複する場合でそれぞれmemcpyとmemmoveを使用してみましたが、今回の実行結果はどちらも結果は同じになりました。
まとめ
- メモリ領域が確実に重複しない事が場合は、memcpyを使用して問題はない。
- メモリ領域が重複する可能性がある場合は、memcpyを使用してはいけない。
- memmoveを使用すればメモリ領域の重複を気にする必要はない。