レガシー言語であるC言語にもたまには楽な所がある。
JavaやC#等のオブジェクト指向言語でいわゆるディープコピーをしようとすると、これが大変めんどくさい。
メンバが大量にあるクラスなどの場合はなおさら。
でも、C言語で構造体の複製を行いたいときは手っ取り早い方法がある。
それがmemcpy!
memcpyの詳細については上のリンク先を見てもらうとして……。
要はメモリ上の領域を丸ごとコピーしてしまえっていうレガシー言語ならではの発想で複製をしようってわけである。
何これ怖い。
構造体の複製はこんな感じで行うことが出来る。
#include <stdio.h> #include <string.h> #include <stdlib.h> //構造体宣言 typedef struct{ int a; char b[16]; }Test; //変数宣言 Test *t1; Test *t2; //中身を表示する void show(){ printf("t1\n"); printf("a=%d, b=%s\n", t1->a, t1->b); printf("t2\n"); printf("a=%d, b=%s", t2->a, t2->b); printf("\n\n"); } int main(){ //領域の確保 t1=(Test*)malloc(sizeof(Test)); t2=(Test*)malloc(sizeof(Test)); //初期化 t1->a=1; strcpy(t1->b, "test"); memcpy(t2, t1, (size_t)sizeof(Test)); show(); printf("t1->aを2に、t1->bをpoppoに変更\n\n"); t1->a=2; strcpy(t1->b, "poppo"); show(); free(t1); free(t2); }
結果は以下の通り。
t1 a=1, b=test t2 a=1, b=test t1->aを2に、t1->bをpoppoに変更 t1 a=2, b=poppo t2 a=1, b=test
コピー後にt1を変更してもt2の内容は変わっていない。
両者とも別の領域にデータが保存されているということが分かる。
しかし、構造体の中にポインタが含まれる場合は注意が必要で……。
#include <stdio.h> #include <string.h> #include <stdlib.h> //構造体宣言 typedef struct{ int a; char *b; }Test; //変数宣言 Test *t1; Test *t2; //中身を表示する void show(){ printf("t1\n"); printf("a=%d, b=%s\n", t1->a, t1->b); printf("t2\n"); printf("a=%d, b=%s", t2->a, t2->b); printf("\n\n"); } int main(){ //領域の確保 t1=(Test*)malloc(sizeof(Test)); t2=(Test*)malloc(sizeof(Test)); //初期化 t1->a=1; t1->b=(char*)malloc(sizeof(16)); strcpy(t1->b, "test"); memcpy(t2, t1, (size_t)sizeof(Test)); show(); printf("t1->aを2に、t1->bをpoppoに変更\n\n"); t1->a=2; strcpy(t1->b, "poppo"); show(); free(t1->b); free(t1); free(t2); }
t1 a=1, b=test t2 a=1, b=test t1->aを2に、t1->bをpoppoに変更 t1 a=2, b=poppo t2 a=1, b=poppo
t2のaは変わらないが、bは変わってしまった。
これは、bはポインタのため、アドレスしか入っていないために起こった。
t1のbには31行目のmallocによって確保されたアドレスが入っていた。
それを33行目のmemcpyでコピーしたので、t2のbにも、t1のbと同じアドレスが入った。
t1のbとt2のbは同じアドレスを指しているため、そのアドレスにある値が変化するとt1のbもt2のbも同じ値を返すということである。
そのため次のように個別にコピーしてあげる必要がある。
#include <stdio.h> #include <string.h> #include <stdlib.h> //構造体宣言 typedef struct{ int a; char *b; }Test; //変数宣言 Test *t1; Test *t2; //中身を表示する void show(){ printf("t1\n"); printf("a=%d, b=%s\n", t1->a, t1->b); printf("t2\n"); printf("a=%d, b=%s", t2->a, t2->b); printf("\n\n"); } int main(){ //領域の確保 t1=(Test*)malloc(sizeof(Test)); t2=(Test*)malloc(sizeof(Test)); //初期化 t1->a=1; t1->b=(char*)malloc(sizeof(16)); strcpy(t1->b, "test"); memcpy(t2, t1, (size_t)sizeof(Test)); //個別コピー t2->b=(char*)malloc(sizeof(16)); strcpy(t2->b, t1->b); show(); printf("t1->aを2に、t1->bをpoppoに変更\n\n"); t1->a=2; strcpy(t1->b, "poppo"); show(); free(t1->b); free(t1); free(t2->b); free(t2); }
t1 a=1, b=test t2 a=1, b=test t1->aを2に、t1->bをpoppoに変更 t1 a=2, b=poppo t2 a=1, b=test
おおレガシーレガシー。
結局、ポインタが多い構造体だと面倒なことにはかわりない、というお話でしたと。
memcpyを使ってみるへのコメント (0)