

下記のコードでなぜlistがlist2もろとも参照地が変わったのか理解できません。  
注意: 私はC#に詳しくありません。

[code]

    private void TestMethod(ref List<String> list2){
    list2 = new List<String>();
    list2.Add("bye-bye!");
    }
    
    List<String> list = new List<String>();
    list.Add("Hello!");
    TestMethod(ref list);
    System.Console.WriteLine(list[0]);    // "bye-bye!"
    
[/code]

`list2 = new
List<String>();`この部分で新しい番地が確保されたのは分かります。しかしlistの参照地まで変わっているようですがどういうメカニズムですか？listとlist2の共通の参照地にlistとlist2という2つの変数の存在と場所の情報を保存しているのですか？

またPHPやjavascriptでどのような挙動になるのか教えてください。（Javaには厳密な参照渡しは存在しておらず”参照地の値渡し（参照地の番地をコピーして渡す）”ということは知っています）


CC BY-SA 4.0
2019-04-29T11:07:57.703
54568
2019-06-05T08:28:29.907
2019-04-29T11:15:24.903
15607
15607
question
python javascript java c#
参照渡しの内部での処理はどうなっているのか


ざっくりとメモリ内の様子の概念図を見てもらうと理解しやすいのではないかと思います。

以下の2行の実行が済むと実行時のスタック・ヒープは(概念的には)以下のような状態になっています。

[code]

    List<String> list = new List<String>();
    list.Add("Hello!");
    
[/code]

[code]

              stack
    list  -> |List<String>への参照 |
               |
               |
         +-----+
         |
         |    heap
         +-> |`list`に参照されるListの中身|
             |[0] "Hello!"             |
    
[/code]

(本当は文字列も参照型だったりするので、参照関係はもっと複雑になるのですが、本質的でない部分は簡略化してあります。)

ここで`TestMethod(ref list);`を呼び出すと、`TestMethod`内の実引数`list2`は以下のような感じになります。

[code]

              stack
    list +-> |List<String>への参照 |
         |     |
      +--+     |
      |        |
      |  +-----+
      |  |
      |  |    heap
      |  +-> |`list`に参照されるListの中身|
      |      |[0] "Hello!"             |
      |
      +----+
           |
    list2 -+
    
[/code]

`list`と`list2`は同じ変数を表している状態になるのです。内部的には変数の番地(アドレス)を渡しています。つまり「参照渡し」というのは、変数への参照を渡すことを表しています。

この状態で`TestMethod`内で、

[code]

    list2 = new List<String>();
    list2.Add("bye-bye!");
    
[/code]

が実行されるとこんな感じ。

[code]

              stack
    list +-> |新List<String>への参照|
         |     |
      +--+     |
      |        |
      |  +-----+
      |  |
      |  |    heap
      |  |   |元`list`に参照されてたListの中身|
      |  |   |[0] "Hello!"                |
      |  |    (↑どこからも参照されないのでそのうち解放される)
      |  |
      |  +-> |`list2`(`list`)に参照されるListの中身|
      |      |[0] "bye-bye!"                    |
      |
      +----+
           |
    list2 -+
    
[/code]

* * *

これに対して、C#で値渡しを使った場合、Javaのように値渡ししかない場合は、呼び出し直後は以下のような感じです。

[code]

              stack
    list  -> |List<String>への参照 |
               |
               |
         +-----+
         |
         |    heap
      +--+-> |`list`に参照されるListの中身|
      |      |[0] "Hello!"             |
      |
      +--------+
               | stack
    list2 -> |List<String>への参照 |
    
[/code]

`list`も`list2`もheap内の同じ実体を指していますが、変数としては別の場所に割り当てられます。

* * *

ざっくりまとめると、

  * 変数そのものへの参照を渡すことにより「同じ変数」を表すことになるのが参照渡し
  * 変数としては別の場所を割り当てられて、その中身だけをコピーするのが値渡し

と言うことになるかと思います。

図も説明も拙いものですが、合わせ技でなんとかご理解頂けるのではないかと期待しています。なにかご不明な点があれば、お知らせください。


CC BY-SA 4.0
2019-04-29T12:27:46.823
54574
2019-04-29T12:27:46.823
13972
54568
answer


C言語、C++をご存じでしたらC#の参照渡しはポインターと考えればすっきりするかもしれません。


CC BY-SA 4.0
2019-06-05T08:28:29.907
55547
2019-06-05T08:28:29.907
24490
54568
answer