質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

Q&A

解決済

5回答

9014閲覧

浅いコピーと深いコピーについて

Tera0724

総合スコア18

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

1グッド

3クリップ

投稿2020/01/31 02:39

編集2020/01/31 02:44

現在、C言語の学習をしています。
その中で、memcoyというメモリ領域をそのままコピーする関数が出てきました。
また、注意欄に
「memcopy関数などでコピーする行為は浅いコピーと言われ、配列や構造体は複製される一方、内部にもつポインタが指している領域まで複製されません。結果としてコピー元とコピー先で同一のものを指し、共有してしまいます。」
とありました。

意味がわからなかったので調べてみたところ、参考記事の中に、
「array1の値を変更すると、なぜかarray2の値も変更されます。これこそが、浅いコピーによる不可思議な現象です。」とありました。
しかし、以下のコードを実行したところ、片方の配列にしか影響が出ません。

C

1#include <stdio.h> 2#include <string.h> 3 4int main(void) 5{ 6 int a[4] = {19, 20, 29, 29}; 7 int b[4]; 8 int b2[4]; 9 memcpy(b, a, 16); 10 memcpy(b2, a, 16); 11 12 printf("配列aの2つ目の要素は%d、番地は%pです\n", a[1], &a[1]); 13 printf("配列bの2つ目の要素は%d、番地は%pです\n", b[1], &b[1]); 14 printf("\n"); 15 16//aの2つ目の要素を1に変更してみる 17 18 printf("配列aの2つ目の要素を1に変更する\n"); 19 a[1] = 1; 20 printf("配列aの2つ目の要素は%d、番地は%pです\n", a[1], &a[1]); 21 printf("配列bの2つ目の要素は%d、番地は%pです\n", b[1], &b[1]); 22 printf("\n"); 23}

「出力結果」
配列aの2つ目の要素は20、番地は0x7ffedfc58954です
配列bの2つ目の要素は20、番地は0x7ffedfc58944です

配列aの2つ目の要素を1に変更する
配列aの2つ目の要素は1、番地は0x7ffedfc58954です
配列bの2つ目の要素は20、番地は0x7ffedfc58944です

私は、浅いコピーはコピー元の要素を変えるとコピー先の要素も変わってしまうと解釈していたのですが、やはり解釈自体に問題があったのでしょうか?
アバウトな質問となってしまい申し訳ありません。

episteme👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答5

0

ベストアンサー

(回答にならない感)

「浅い」「深い」という言葉を知っておくことは大切かもしれませんが,そういった言葉に振り回されるよりも前に,
memcpy(b, a, 16);によって,あなたが「何をコピーしたのか」を把握することの方が重要です.

memcpy(b, a, 16);自体は,「aが指す位置から16バイト分の内容をbが指す位置にコピー」することでしかないわけで,
「aが指す位置から16バイト分の内容」ってのは実際には(そのプログラムにおいては)何なのか,それはどのような性質のものなのか,という側の理解が(浅いとか深いとか言い出すよりも前に前提知識として)先に必要です.

それがわかってさえいれば,コピー元とコピー先の関係性は自明であり,「memcpyとさえ書けばコピー先とコピー元とに変な関係が結ばれる…?」みたいな誤解をすることはないハズです.

(ちょいと「ポインタとは」的な復習をされると良いのではないかな,と)

投稿2020/01/31 04:54

fana

総合スコア11656

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

fana

2020/01/31 05:11

単に言葉を知らない場合なら, 誰か「だからー,その×××のことを"シャローコピー"って言うんだよ,まったくー」 ぼく「そうなのか」(←ちょっとはずかしい) くらいの話で済むけど, まず"xxx"の内容側がわかってないとどうにもならない,的な.
rubato6809

2020/02/03 00:32

ポインタ変数も、構造体も、配列も・・・メモリ上に在る・配置されるわけで、 そのメモリ上の姿をできるだけ具体的にイメージできることは、C言語では特に重要なことだと思っています。 「実際には何なのか,それはどのような性質のものなのか,という理解が先に必要」というお答えに我が意を得たりと思いました。
guest

0

「memcopy関数などでコピーする行為は浅いコピーと言われ」という表現自体が酷く不適切です。
浅いコピー(シャローコピー)はコピーしたいもの自体でなく、その参照をコピーする事を言います
深いコピー(ディープコピー)はコピーしたいものの実体をコピーすることです。
したがって質問者さんのソースコードのint配列コピーは深いコピーです。
配列の場合浅いコピーは
int* c = a;
のような形になります。

構造体をmemcpyしたときに中の要素にポインタが含まれていた場合にその参照先をディープコピーしないことがあると言いたいのだと思いますが、「memcpy自体がシャローコピーである」というのはとんでも解説なので本当にそのように書かれていたのであれば勉強資料を選び直すことを検討した方が良いかと思います。

【追記】
コメントを受け、紛らわしい部分を修正しました。
加えて上記ディープコピーとシャローコピーについての解釈は私見であることも一応記載しておきます。

投稿2020/01/31 03:07

編集2020/01/31 09:51
退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Zuishin

2020/01/31 03:44

ポインタを含む構造体の配列のコピーは実体のコピーですが、これはシャローコピーだと思います。memcopy は外側しかコピーしないのでシャローコピーと言って差し支えないと思いますが、どこかにこの回答を裏付ける資料がありますか?
momon-ga

2020/01/31 06:11 編集

言いたいことは、推測できるけど書いてる内容が違いますよね。 ポインタの実体は、Zuishinさんのいう通り、「ポインタそのもの」であって、「ポインタの参照先」じゃないですよね? ちなみに、代入のことを浅いコピーっていうのは、結構おおいの? 代入するもので、代入演算子が浅いコピーだったり深いコピーになるって難しくない? ポインタそのものの深いコピーってか、代入なだけじゃないの?
退会済みユーザー

退会済みユーザー

2020/01/31 09:15

>Zuishinさん >これはシャローコピーだと思います。memcopy は外側しかコピーしないのでシャローコピーと言って差し支えないと思いますが すいませんが、「これ」と「外側」の指す先を教えていただけませんでしょうか。 多分ちょっとした認識のすれ違いが発生しているだけなのではないかなと思っています。 >momon-gaさん 回答するときに「ポインタと参照の違い」に関するコメントがくるかもとは思いましたが案の定でした。 ポインタ自体の実体コピーであることは分かった上で回答の言い回しになっています。 これに関しては「代入するもの」ではなく「何を主体に置いているか」で言い回しが変わると私は解釈しています。 例えば配列の代入によるコピーの場合は、配列というもの自体はシャローコピーされていて、配列の先頭ポインタはディープコピーされていると捉えています。 ポインタの配列をmemcpyした場合は配列の要素であるポインタはディープコピーされたが、その参照先はシャローコピーされている、と考えています。 でもこの辺は極論最初に言葉を作った人がどう定義したか、この件についての公式な文献が有ればどう言っているか、和訳は適切か、一般的にはどう認知されているか、とかそういう議論にならざるを得ないと思うんですよね。 日本で参照渡しとポインタの実体渡しが混同されているという事実は存じておりますが、コピーについては別問題で「参照をコピーしたか否か」ではなく「コピーしようとした対象自体が複製されたのか」が論点かと考えています。
Zuishin

2020/01/31 09:24

> ポインタを含む構造体の配列のコピー が「これ」です。 議論をしたいのではなく、「シャローコピーとは」という信頼できる定義がどこかにあっての断言かと思って聞きました。私の認識しているシャローコピーとは少し違うようですが、私の方も確たるよりどころがあるわけではないので、ここまでにしておきます。
退会済みユーザー

退会済みユーザー

2020/01/31 09:31

気になるのでこの手の話を誰かの解釈という域を越えて厳密に定義した文献があれば是非見てみたいのですが・・・どなたかご存じないでしょうか?
退会済みユーザー

退会済みユーザー

2020/01/31 09:46 編集

>Zuishinさん ありがとうございます! やっぱり単に認識の相違があっただけで、私もそれはシャローコピーかと思います(構造体の一部要素に限ればディープコピー) 回答の >したがって質問者さんの配列コピーは深いコピーです。 はソースコードのint配列を指した言葉でした。紛らわしくて申し訳ございません(修正しておきます) 後半のお話はmomon-gaさん向けの奴ですので気になさらないで下さい(議論ふっかけるとかではないです、すいません)
guest

0

私は、浅いコピーはコピー元の要素を変えるとコピー先の要素も変わってしまうと解釈していたのですが、やはり解釈自体の問題があったのでしょうか?

はい。引用元に「配列や構造体は複製される一方、内部にもつポインタが指している領域まで複製されません。」とあるように、配列や構造体がポインタを含んでいた場合の問題です。単なる数値配列であれば無関係です。

投稿2020/01/31 02:43

maisumakun

総合スコア145183

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

maisumakun

2020/01/31 02:52

参考記事での「浅いコピー」は、memcpyの「浅いコピー」とは別物です。
guest

0

memcpy = repz movsx によるコピーに浅いも深いもありません。
実直に指定アドレスから指定長を書き込み先に複写します。
複写元が文字列なのか、ポインターなのか、それとも純粋な整数なのか、それは設計都合でしかありません。
ここでのシャロウ/ディープ解説は、解説者がよく知るVMの概念を持ち込もうとして失敗しています。
アセンブラーまたはCとその後継言語は、memcopyの利用時にはその時そこにあった値を切り出すだけで、ポインターの指し先が変わったからといって追従するよう補完してくれたりはしません。

インタープリター自体の実装は大したものだと思いますが、そこへの実装は、マニュアル車とAT車の操作程度には違います。

投稿2020/02/05 00:01

編集2020/02/05 00:34
kendji

総合スコア92

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Zuishin

2020/02/05 00:25

VM とは何でしょう? インタープリターは何を指していますか?
kendji

2020/02/05 00:29

VMは仮想マシンなんですが、一般にはCPUなどのプラットフォームに依存しないコードが書けるように設計されたものです。石がIntelだろうがARMだろうが動きます。 で、いろいろ議論はありますが、VMもいろいろあるインタープリターの一つです。
Zuishin

2020/02/05 00:32

わかりました。回答として意味不明なので低評価します。
fana

2020/02/05 01:21

同上.(特に最後の文とか何を指して何を言っているのか)
guest

0

浅いコピーは一般的に、参照のみのコピー

C++

1int a; 2int b; 3&a = &b;

みたいなの。

深いコピーは一般的に、中身までコピー(ただし、ポインタの先はコピーしない)

C++

1int a; 2int b; 3a = b; 4 5int *c; 6int *d; 7c = d;

という感じ

と、思いますが、質問者さんのみた記事は、上記の c = d も浅いコピーと表現されているようです。
この辺りに注意して、ポインタを意識して考えてみてはどうでしょうか

投稿2020/02/04 02:36

ttb

総合スコア67

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

fana

2020/02/04 03:10 編集

オブジェクトを指し示す方法が参照かポインタかによって「深い」「浅い」の呼び方が変わる,という話に見えますが,そういう意味でしょうか? (あと,前半のコード内の &a = &b; は書き間違いでしょうか)
ttb

2020/02/04 04:17

すみません、int a に、「&a =」での変更不可でした。ちょっと整理し直します。
ttb

2020/02/04 04:55 編集

要は、実体(要素)をコピーする場合は深いコピーで、参照(番地,アドレス)をコピーするだけの場合浅いコピーと言われます。 int a; int *p1; int *p2; a = 1; // 一般的に浅いコピー。参照(アドレス)のみコピー p1 = &a; // 一般的に深いコピー。参照の先の実体まで別途用意してコピー p2 = new int; *p2 = a; *p1 = 2; // ← 浅いコピーしたので、aの値も「2」になる *p2 = 3; // ← 深いコピーしたので、aとは関係がなくなり、aの値は変化しない // 上でnewしているので最後に delete p2; を忘れないように ただ、構造体内参照の話となると、解釈によって、どこまでを浅いと呼ぶかが変わる印象です。 一般的に、構造体を = で代入するなど、実体のコピーを伴えば深いコピーと言われると思いますか、質問者さんの読んだ記事では、どちらかというと浅いコピーと表現しているようです。 (あとで再コメント予定)
ttb

2020/02/04 05:36 編集

ちょっとコード汚いですがご容赦を。 質問者さんの読んだ記事では、下の構造体 STP をmemcpy()するときに「一部浅いコピーになる」という事を言いたかったんでしょう。 ただ、下記のSTをmemcpy()しているところは「深いコピー」しています。 結局、memcpy()は、浅いコピーも深いコピーも使い方次第ということになり、みなさんが「memcpy()は浅いコピーではない」と言っているところかと思います。 ちょっと話がややこしくなりすぎたでしょうか。 結局のところ、質問者さんの解釈の方向性は間違っていないと思います。 質問者さんの読んだmemcpy()解説は、ある意味ではあっているものの、正確ではない書き方をしているように思います。 「浅いコピーはコピー元の要素を変えるとコピー先の要素も変わってしまう」 上記は、結果的にはそうなのですが、本質がやや異なるので、他の回答者さんも引っかりがあるところなんだと思います。 私なりに「浅いコピーは、参照(番地、アドレス)のみコピーすること。(実体(要素)は、コピーしない、または一部しかコピーされない。)」と表現しておきます。 ----- struct ST { int a; int b; }; struct ST st1; struct ST st2; struct ST *pst1; st1.a = 1; st1.b = 2; st2 = st1; // ←深いコピー memcpy(&st2, &st1, sizeof(struct ST)); // ←深いコピー (これを浅いコピーと読めてしまう記事なのでどうかと) pst1 = &st1; // ←浅いコピー // 参照を持った構造体の場合・・・? struct STP { int a; int *p; }; int hoge; struct STP st10; struct STP st20; struct STP st30; struct STP *pst; hoge = 100; st10.a = 100; st10.p = &hoge; pst = &st10; // ←浅いコピー st20 = st10; // ←代入なので一般的に「深いコピー」? .pに着眼すると浅いコピー とも言える memcpy(&st20, &st10, sizeof(struct STP)); // ←これも上記と同じ動作のため、単に「浅いコピー」と呼んでいいものか // 深いコピー st30.a = st10.a st30.p = new int; *st30.p = *st10.p; // delete st30.p;を忘れないこと
LouiS0616

2020/02/04 05:43

Cの場合ポインタも限定的に演算可能な数値なので、実体と参照を無理に区別しようとするとかえって分かりづらくなるように思います。
fana

2020/02/04 08:13

「浅い」「深い」は,個人的には,コードの形で区別するのでなくて,処理の意味の側について呼ぶ言葉だと思っています. あるデータ構造(「型」と言ってもよいかな?)について,そのデータを扱う際の意味的にデータ共有状態になるようなコピーの仕方を「浅い」と言う…感じ.
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問