レガシー言語である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)