C言語でmemcpy関数とmemmove関数の違いを教えて!
こういった悩みにお答えします.
本記事の信頼性
- リアルタイムシステムの研究歴12年.
- 東大教員の時に,英語でOS(Linuxカーネル)の授業.
- 2012年9月~2013年8月にアメリカのノースカロライナ大学チャペルヒル校(UNC)コンピュータサイエンス学部で客員研究員として勤務.C言語でリアルタイムLinuxの研究開発.
- プログラミング歴15年以上,習得している言語: C/C++,Python,Solidity/Vyper,Java,Ruby,Go,Rust,D,HTML/CSS/JS/PHP,MATLAB,Verse(UEFN), Assembler (x64,ARM).
- 東大教員の時に,C++言語で開発した「LLVMコンパイラの拡張」,C言語で開発した独自のリアルタイムOS「Mcube Kernel」をGitHubにオープンソースとして公開.
- 2020年1月~現在はアメリカのノースカロライナ州チャペルヒルにあるGuarantee Happiness LLCのCTOとしてECサイト開発やWeb/SNSマーケティングの業務.2022年6月~現在はアメリカのノースカロライナ州チャペルヒルにあるJapanese Tar Heel, Inc.のCEO兼CTO.
- 最近は自然言語処理AIとイーサリアムに関する有益な情報発信に従事.
- (AI全般を含む)自然言語処理AIの論文の日本語訳や,AIチャットボット(ChatGPT,Auto-GPT,Gemini(旧Bard)など)の記事を50本以上執筆.アメリカのサンフランシスコ(広義のシリコンバレー)の会社でプロンプトエンジニア・マネージャー・Quality Assurance(QA)の業務委託の経験あり.
- (スマートコントラクトのプログラミングを含む)イーサリアムや仮想通貨全般の記事を200本以上執筆.イギリスのロンドンの会社で仮想通貨の英語の記事を日本語に翻訳する業務委託の経験あり.
こういった私から学べます.
C言語を独学で習得することは難しいです.
私にC言語の無料相談をしたいあなたは,公式LINE「ChishiroのC言語」の友だち追加をお願い致します.
私のキャパシティもあり,一定数に達したら終了しますので,今すぐ追加しましょう!
独学が難しいあなたは,元東大教員がおすすめするC言語を学べるオンラインプログラミングスクール5社で自分に合うスクールを見つけましょう.後悔はさせません!
目次
memcpy関数とmemmove関数の違い
1 |
void *memcpy(void *dest, const void *src, size_t n); |
memcpy関数は,メモリ領域srcの先頭nバイトをメモリ領域destにコピーします.
コピー元の領域とコピー先の領域が重なってはいけません.
正常にコピーできない可能性があり,未定義の動作になります.
memcpy関数の返り値は,destへのポインタです.
1 |
void *memmove(void *dest, const void *src, size_t n); |
memmove関数は,メモリ領域srcの先頭nバイトをメモリ領域destにコピーします.
memcpy関数とは異なり,memmove関数はコピー元とコピー先のメモリ領域が重なってもよいです.
最初にsrcのバイトがsrcともdestとも重ならない一時的な配列にコピーされてから,一時的な配列からdestにバイトのコピーが行われたかのように動作します.
※一時的な配列がなくてもmemmove関数は実装可能です.自作関数で紹介します.
その代わり,memmove関数はmemcpy関数より実行時間が長いというデメリットがあります.(実際には無視できるくらいです.)
memmove関数の返り値は,destへのポインタです.
メモリ領域ではなく文字列をコピーするstrcpy関数の使い方を知りたいあなたはこちらからどうぞ.
memcpy関数とmemmove関数の利用例
memcpy関数とmemmove関数の利用例は以下になります.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
/* * Author: Hiroyuki Chishiro * License: 2-Clause BSD */ #include <stdio.h> #include <string.h> #define BUFSIZE 64 #define BEGIN 3 #define N 5 int main(void) { char str[] = "0123456789"; char s[BUFSIZE], t[BUFSIZE]; size_t len; len = strlen(str) + 1; memcpy(s, str, len); memcpy(t, str, len); printf("memcpy(): s = %s, t = %s\n", s, t); memmove(s, str, len); memmove(t, str, len); printf("memmove(): s = %s, t = %s\n", s, t); memcpy(s + BEGIN, s, N); printf("memcpy(): s = %s\n", s); memcpy(s, s + BEGIN, N); printf("memcpy(): s = %s\n", s); memmove(t + BEGIN, t, N); printf("memmove(): t = %s\n", t); memmove(t, t + BEGIN, N); printf("memmove(): t = %s\n", t); return 0; } |
GCCでコンパイルした場合の実行結果は以下になります.
3~4行目のsとtの文字列は,コピーするメモリ領域が重なっていないので,同じ「0123456789」になっていることがわかります.
5~6行目のmemcpy関数と7~8行目のmemove関数の結果は,コピーするメモリ領域が重なっているので異なる結果になると思ったのですが,同じ結果(それぞれ「0120123489」と「0123423489」)になっていますね...
GCCではmemcpy関数とmemmove関数が同じ結果になりました.
1 2 3 4 5 6 7 8 |
$ gcc memcpy_and_memmove.c $ a.out memcpy(): s = 0123456789, t = 0123456789 memmove(): s = 0123456789, t = 0123456789 memcpy(): s = 0120123489 memcpy(): s = 0123423489 memmove(): t = 0120123489 memmove(): t = 0123423489 |
Clangでコンパイルした場合の実行結果は以下になります.
3~4行目と7~8行目の実行結果はGCCでコンパイルした場合と同様です.
5行目は「0120123189」で6行目は「0123123189」と,7~8行目とそれぞれ異なる結果になりました.
つまり,GCCとClangで動作結果が異なりますので,コピーするメモリ領域が重なる場合はmemmove関数を利用しましょう!
1 2 3 4 5 6 7 8 |
$ clang memcpy_and_memmove.c $ a.out memcpy(): s = 0123456789, t = 0123456789 memmove(): s = 0123456789, t = 0123456789 memcpy(): s = 0120123189 memcpy(): s = 0123123189 memmove(): t = 0120123489 memmove(): t = 0123423489 |
参考までにVisual Studioでビルドした場合の実行結果は以下になります.GCCと同様です.
1 2 3 4 5 6 7 8 9 |
memcpy(): s = 0123456789, t = 0123456789 memmove(): s = 0123456789, t = 0123456789 memcpy(): s = 0120123489 memcpy(): s = 0123423489 memmove(): t = 0120123489 memmove(): t = 0123423489 *.exe (プロセス *) は、コード 0 で終 了しました。 このウィンドウを閉じるには、任意のキーを押してください... |
memcpy関数とmemmove関数の自作関数
memcpy関数とmemmove関数の自作関数は以下になります.
mymemcpy関数はsrcの先頭からdestにコピーするため,コピーするメモリ領域が重なった場合(かつdestのアドレスがsrcのアドレスより大きい場合)は正常にコピーできません.
これに対して,mymemmove関数では,29行目のif文でdestのアドレスとsrcのアドレスを比較します.
destのアドレスsrcのアドレス以下の場合,srcの先頭からdestにコピーし,そうでなければsrcの末尾からdestにコピーします.
なので,mymemmove関数はコピーするメモリ領域が重なった場合でも正常にコピーできます.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
/* * Author: Hiroyuki Chishiro * License: 2-Clause BSD */ #include <stdio.h> #include <string.h> #define BUFSIZE 64 #define BEGIN 3 #define N 5 void *mymemcpy(void *dest, const void *src, size_t n) { char *d = (char *) dest; char *s = (char *) src; while (n--) { *d++ = *s++; } return dest; } void *mymemmove(void *dest, const void *src, size_t n) { char *tmp; const char *s; if (dest <= src) { tmp = (char *) dest; s = (char *) src; while (n--) { *tmp++ = *s++; } } else { tmp = (char *) dest; tmp += n; s = (char *) src; s += n; while (n--) { *--tmp = *--s; } } return dest; } int main(void) { char str[] = "0123456789"; char s[BUFSIZE], t[BUFSIZE]; size_t len; len = strlen(str) + 1; mymemcpy(s, str, len); mymemcpy(t, str, len); printf("mymemcpy(): s = %s, t = %s\n", s, t); mymemmove(s, str, len); mymemmove(t, str, len); printf("mymemmove(): s = %s, t = %s\n", s, t); mymemcpy(s + BEGIN, s, N); printf("mymemcpy(): s = %s\n", s); mymemcpy(s, s + BEGIN, N); printf("mymemcpy(): s = %s\n", s); mymemmove(t + BEGIN, t, N); printf("mymemmove(): t = %s\n", t); mymemmove(t, t + BEGIN, N); printf("mymemmove(): t = %s\n", t); return 0; } |
実行結果は以下になります.
3~4行目と7~8行目の実行結果は同様です.
5行目は「0120120189」,6行目は「0120120189」と7~8行目とClangの場合と異なる結果になりましたが,正常にコピーできなかったという点は同様です.
1 2 3 4 5 6 7 8 |
$ gcc mymemcpy_and_mymemmove.c $ a.out mymemcpy(): s = 0123456789, t = 0123456789 mymemmove(): s = 0123456789, t = 0123456789 mymemcpy(): s = 0120120189 mymemcpy(): s = 0120120189 mymemmove(): t = 0120123489 mymemmove(): t = 0123423489 |
まとめ
C言語でmemcpy関数とmemmove関数の違いと自作関数を紹介しました.
コピーするメモリ領域が重なった場合,memcpy関数とmemmove関数は,GCCとVisual Studioでは同じ結果,Clangでは異なる結果になりました.
memcpy関数とmemmove関数の違いは下表になります.
※実行速度の違いは実際には無視できるくらいです.
項目 | memcpy関数 | memmove関数 |
---|---|---|
メモリ領域が重なった場合 | 正常にコピーできない可能性がある (未定義の動作) GCC/Visual Studioは正常に動作 Clangは未定義の動作 | 正常にコピーできる |
実行速度 | 速い | 遅い |
memcpy関数と似ているmemccpy関数を知りたいあなたはこちらからどうぞ.
C言語を独学で習得することは難しいです.
私にC言語の無料相談をしたいあなたは,公式LINE「ChishiroのC言語」の友だち追加をお願い致します.
私のキャパシティもあり,一定数に達したら終了しますので,今すぐ追加しましょう!
独学が難しいあなたは,元東大教員がおすすめするC言語を学べるオンラインプログラミングスクール5社で自分に合うスクールを見つけましょう.後悔はさせません!