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

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

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

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

.NET Framework

.NET Framework は、Microsoft Windowsのオペレーティングシステムのために開発されたソフトウェア開発環境/実行環境です。多くのプログラミング言語をサポートしています。

Q&A

解決済

4回答

712閲覧

C#の参照型の値渡しについて

tmtmshohei

総合スコア16

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

.NET Framework

.NET Framework は、Microsoft Windowsのオペレーティングシステムのために開発されたソフトウェア開発環境/実行環境です。多くのプログラミング言語をサポートしています。

0グッド

0クリップ

投稿2018/07/27 08:24

前提・実現したいこと

参照型の値渡しをした際の挙動について質問です。

下記のようなコードがある場合、
関数呼び出し時に、新たに仮引数a,b分のメモリを確保し、
そこに変数hogeとhugaのアドレスがコピーされるという認識でいます。

そうするとhugaのアドレスが入っているbをaに代入したら
a.xは20になるように見てえしまうのですが、
なぜa.x=10のままになるのでしょうか?
(hogeとhoge.xで使っているメモリが違うのでしょうか?)

どなたかご教示ください。
宜しくお願い致します。

該当のソースコード

C#

1using System; 2 3class Test 4{ 5 class Hoge{public int x;} 6 7 public static void Main() 8 { 9 var hoge = new Hoge(); 10 hoge.x=1; 11 var huga = new Hoge(); 12 huga.x=2; 13 Cal(hoge,huga); 14 Console.WriteLine(hoge.x);//10 15 Console.WriteLine(huga.x);//20 16 } 17 18 static void Cal(Hoge a,Hoge b) 19 { 20 a.x=10; 21 b.x=20; 22 a=b; 23 } 24 25}

補足情報(FW/ツールのバージョンなど)

ここにより詳細な情報を記載してください。

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

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

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

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

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

guest

回答4

0

ベストアンサー

こんにちは。

関数呼び出し時に、新たに仮引数a,b分のメモリを確保し、

そこに変数hogeとhugaのアドレスがコピーされる

残念ながら間違っている記述です。正しくは以下の通りです。

関数呼び出し時に、新たに仮引数a,b分のメモリを確保し、

そこに変数hogeとhugaに入っている(newで生成されたオブジェクトの)アドレスがコピーされる

ここが分かれば、疑問も解消するだろうと思います。

また、もし既にC言語のポインタを理解されているのであれば、C#の参照型はC言語のポインタとほぼ同じ振る舞いをすると考えると良いです。

投稿2018/07/27 09:19

Chironian

総合スコア23272

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

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

tmtmshohei

2018/07/27 11:00

ポインタに関しては昔ざっとリファレンスを読んだだけなのでうろ覚えですが *p = &a のような形でアドレスを入れてpでもaの値が変更できるようになるくらいのイメージで大丈夫でしょうか? まず私の参照型の変数の解釈がごちゃごちゃになっているのかもしれません。 変数hogeとhogeに代入されているインスタンスのアドレスは違うということでしょうか?
Chironian

2018/07/27 12:37

C言語のポインタは「ポインタ型の変数」で、その変数の内容はポイント先のオブジェクトのアドレスです。 C#の参照も「参照型の変数」で、その変数の内容は参照先のオブジェクトのアドレスです。 これをメモリのイメージとして思い描ければ十分かと思います。 例えば、↓のようなイメージです。 https://theolizer.com/cpp-school1/cpp-school1-11/
pepperleaf

2018/07/27 12:39

> その変数の内容は参照先のオブジェクトのアドレスです。 アドレスなんですか? ガベージとかで参照先が変わるので、アドレスでは無いと聞いた気が、、。
Chironian

2018/07/27 12:43

ああ、確かにアドレスではなくてハンドルですね。 今その話を始めるとますます混乱しそうですので多めに見てくださいな。
tmtmshohei

2018/07/28 04:56

ありがとうございます。 関数でhogeを渡した時に、hogeの参照先の情報まで渡しているのかと勘違いしていたのが問題でした。 hogeの参照先のイメージについてなのですが、例えば Hogeクラスがint x,y;となっていた場合、hoge=new Hoge();をすると hoge.xとhoge.yでは当然アドレス?は変わってくるのでしょうか?
Chironian

2018/07/28 06:33

その通りです。同じアドレス(ハンドル)だったら、同じメモリを指します。異なるメモリに割り当てられて変数であればアドレス(ハンドル)も異なります。
guest

0

a,bは関数内のスコープなので、その確保されたメモリは}を出た時点で破棄されますよね(実際その場で破棄されるかはともかく、そういう動作をする)。
末尾でa=b;だけしても、何ら影響を及ぼすことがないです(refとかをすれば別かもしれないですが)。

投稿2018/07/27 08:33

papinianus

総合スコア12705

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

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

papinianus

2018/07/27 08:36

a=b;をした後にa.Xとすれば20になるでしょうが、hogeにhugaのアドレスを入れる操作をしていないので、hoge.Xは10
papinianus

2018/07/27 09:56 編集

hogeが持つ参照アドレスを1、hugaが持つ参照アドレスを2だとしましょう。 Calの{を1行目だとしたとき、0行目でaが1で、bが2。 2行目で参照アドレス1のXが10、3行目で参照アドレス2のXが20になります。 4行目でaが持つ参照アドレスが2になりリターン。 さてhogeの参照アドレスはなんでしょうか? 参照アドレス1のXは? 参照アドレス2のXは? 質問に書いておられるとおり、aはhoge(参照)の値(アドレス)を渡されているのであってhoge(参照)が何を指しているかの構造・hoge自身の番地(参照)を渡されているのではないので。
tmtmshohei

2018/07/27 10:58

hogeの参照アドレスは1、アドレス1.xは10,アドレス2.xは20であっていますでしょうか? 私の参照型の変数の解釈がごちゃごちゃになっているのかもしれません。 変数hogeとhogeに代入されているインスタンスのアドレスが違うということでしょうか?
papinianus

2018/07/27 11:24

その理解で正しいです。hogeが1を参照し、アドレス1.Xが10なので、10なのです。hogeが何を指しているかはa=b;ではかわりませんよね。 hoge(という変数自身)もメモリ上に存在する以上、何らかのアドレスをもっていて、そのhogeの番地にある値を(hogeが参照型なので)番地と解釈して、その番地のオブジェクトを取ってくるのが、hogeという変数の働き。 Cal(hoge, huga)と呼び出しされたとき、aはaで番地を持ち、Calの関数内では、aはa番地に、hoge番地にあった値をコピーしてもらうことになります。aにhoge番地が入るわけではないです。なので、a番地の値をhuga番地にあってbにコピーされた値(前記では2)をa番地にいれても、hoge番地にある値はかわりませんので、hoge.Xという呼び出しをすれば、10のまま。 a=bとして、hoge側が変わるためには、a番地にはhoge番地が入り、かつそれはオブジェクトそのものではなく番地と解釈してその先のオブジェクトを触れるように宣言しないといけない。それがポインタというような仕組みです(C#のrefがどうかはすみませんが存じません)
papinianus

2018/07/27 11:28

excelと、excelに名前の定義というのがあるのをご存じだったら、多少通じるのかもしれない。 名前としてのhogeは、それ自身がどこかしらのセル(A2)をさし、そのA2に"1"が入っていたり"もじれつ"が入っていたりする。 aに参照の値渡しがおきるとき、aにはA2というセルが渡され、aをC4にしたりすることがあっても、hogeという名前がA2を指している関係が変動するわけではない。
tmtmshohei

2018/07/28 04:57

ご丁寧にありがとうございます。 関数でhogeを渡した時に、hogeの参照先の情報まで渡しているのかと勘違いしていたのが問題でした。 上の方の回答で保存されているのはアドレスではなく〜のようなお話が出ていたので その辺りもう少し調べてみようと思います。
guest

0

static void Cal(Hoge a,Hoge b)

ここで渡されるのは、a と bの参照。 a と b の実体は、別のところに確保されています。(まあ、アドレスと言っても良いですが、動的に変化する可能性があるので、実は違う)

a.x=10;

これば、aの実体に含まれる x の値の更新 (x <= 10)

a=b;

これは、メソッド Cal() 内で 参照 a の値を 参照 b に設定している。 ==> 従って、呼び出し側には関係しない話。

他の方と似たり寄ったりとなっていますが、どうでしょう。


参照は、アドレスでなく、ハンドル? だったか。 ラベルと言っても良いかも。

Main() で Hoge, Huga と名付けられているオブジェクトの実体を Obj1, Obj2 とします。
--> Main()では、 Obj1 は、Hoge, Obj2 は Huga の名前(ラベル)で呼ばれる。これは、 Cal()の引数として渡され、Cal()内では、 a, b と呼ばれる。 (a <-- Obj1, b <-- Obj2)
ここで、 a = b の処理は、 Obj2 に a と言う名前(ラベル)を付ける事に相当します。これは、 Cal()内での話で、Main()での Hoge, Huga にはなんの関係もありません。
これに対し、a.x = 10 は、aと言う名前(ラベル)を持つ Obj1 の中にある xを変更する処理となります。

なぜ、アドレスと言うのが間違いかと言うと、Obj1 と Obj2 を管理しているのは別(OS?) なので、
Obj1, Obj2 はその時々の都合でどこに置かれるか分からないから。例えるなら、最初、倉庫A にあったが、一杯になったので、倉庫Bに移される事があるから。で、Obj1 を参照するという事は、倉庫の管理人に対し、 Obj1 を要求する事になります。 (と、書いていたら、i-node と一緒? て気がしてきた)

通常、気にする必要はありませんが、アンマネージドの C++ とI/Fする時に、問題となり、メモリをロック(勝手に倉庫を動かすな、と言う指定)しないと不正な結果となります。

投稿2018/07/27 12:12

編集2018/07/27 13:50
pepperleaf

総合スコア6383

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

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

tmtmshohei

2018/07/28 04:58

ありがとうございます。 関数でhogeを渡した時に、hogeの参照先の情報まで渡しているのかと勘違いしていたのが問題でした。 hogeの参照先のイメージについてなのですが、例えば Hogeクラスがint x,y;となっていた場合、hoge=new Hoge();をすると hoge.xとhoge.yでは当然アドレス?は変わってくるのでしょうか?
pepperleaf

2018/07/28 05:43 編集

new した時点で、新しい領域が確保されます。 ただし、アドレスが、、、という観点で言うと、古い領域が未使用(未参照)になれば、再利用される可能性があるため、完全に別とは言い切れまん。
guest

0

a=b;

bのアドレスをaにコピーしてるだけ。内容はコピーされてないってだけの話しですね


そこの a、b ってのはアドレス値(クラスの参照)ってのはわかってますよね?

投稿2018/07/27 08:55

編集2018/07/27 08:57
y_waiwai

総合スコア87719

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

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

tmtmshohei

2018/07/27 09:10

はい、aとbにはhogeとhugaのアドレスが入っているということですよね? だからこそ関数内で値を変更してもそれが反映されているのですよね。 単にhogeとhoge.xではアドレスが違うのでしょうか?
y_waiwai

2018/07/27 09:14

アドレスがコピーされるだけ、aとbは同じところを指すようになるだけで、その内容(アドレスが指している先のデータ)は変わりません。
tmtmshohei

2018/07/27 11:02

重ね重ね申し訳ありません。 ここで関数に渡されているのは変数hogeのアドレスであり、hogeに代入されたインスタンスのアドレスではないということでしょうか?
y_waiwai

2018/07/27 11:29 編集

> hogeに代入されたインスタンスのアドレス です hogeに代入されたアドレスが、aに代入されて関数が呼ばれます
tmtmshohei

2018/07/28 04:56

ありがとうございます。 関数でhogeを渡した時に、hogeの参照先の情報まで渡しているのかと勘違いしていたのが問題でした。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問