そらのうきぶくろ

Blog - Permalink

memcpyを使ってみる

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

コメントを書く


You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>