C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[4]: 参照型の参照渡し


(過去ログ 128 を表示中)

[トピック内 10 記事 (1 - 10 表示)]  << 0 >>

■76089 / inTopicNo.1)  参照型の参照渡し
  
□投稿者/ ATM (1回)-(2015/06/01(Mon) 04:06:11)

分類:[C#] 

参照型の参照渡しをした時メソッド側で参照型の参照情報を変更すると呼び出し元の参照情報も変更されるという解説がありました。

私の解釈では上記の解説だと合点がいかないのです。以下に解釈を記述します。間違いがあればご指摘お願いいたします。

[呼び出し元]
参照型の変数はスタックメモリ領域(aaaa)に確保されている。
参照型の変数のデータの実体はヒープメモリ領域(xxxx)に確保されている。
参照型の変数はヒープメモリ領域のメモリ番地(xxxx)を格納している

[メソッド側]
参照型の引数はスタックメモリ領域(bbbb)に確保されている。
参照型の引数は参照型を参照渡しされているので参照情報の参照をしている。
参照型の引数はスタックメモリ領域のメモリ番地(aaaa)を格納している ← ここが特に知りたい


メソッド側の引数に別のデータの実体のあるメモリ番地(YYYY)の参照情報を代入すると
参照先である呼び出し元の変数の参照情報(xxxx)が変更されるのであってメソッド側の引数の参照情報はメモリ番地(aaaa)のままで変更されないのではないかと今のところ解釈しています。

メソッド側の参照情報を変更すると呼び出し元の参照情報も変更されるという解説は正しいのでしょうか



引用返信 編集キー/
■76090 / inTopicNo.2)  Re[1]: 参照型の参照渡し
□投稿者/ Azulean (489回)-(2015/06/01(Mon) 06:39:33)
例として下記のようなクラスとメソッドがあったとします。

class Sample { }
void NormalMethod(Sample s);
void ReferenceMethod(ref Sample s);

NormalMetehod においては大筋合っていると思います。
読まれていた記事の「参照型変数の参照渡し」は ReferenceMethod のように「ref キーワードを使うこと」なのでは?
引用返信 編集キー/
■76092 / inTopicNo.3)  Re[1]: 参照型の参照渡し
□投稿者/ 魔界の仮面弁士 (355回)-(2015/06/01(Mon) 09:55:24)
No76089 (ATM さん) に返信
> という解説がありました。
それは、どこにあった解説でしょうか?

前後の文章まで読まないと、別の意味に解釈されてしまう可能性も
ありますので、出典を明らかにしていただけると助かります。


> 参照型の参照渡しをした時メソッド側で参照型の参照情報を変更すると呼び出し元の参照情報も変更される

上記の文章は、下記の Method1〜Method4 のうち、いずれの動作を指しているのでしょうか。

class Program
{
 // 「参照型」の実験用に、ジャグ配列を用意しました。
 static byte[][] ary = new byte[][] {
  new byte[] { 0x11, 0x12, 0x13 },
  new byte[] { 0x21, 0x22, 0x23, 0x24 },
  new byte[] { 0x31, 0x32, 0x33, 0x34, 0x35 },
  new byte[] { 0x41, 0x42, 0x43, 0x44, 0x45, 0x46 },
 };

 static void Main()
 {
  byte[][] original = ary;

  Console.WriteLine("=== 最初の状態 ===");
  Array.ForEach(ary, b => Console.WriteLine(BitConverter.ToString(b)));

  Console.WriteLine("=== Method1(参照型の参照渡し, メンバー編集) ===");
  Method1(ref ary);
  Array.ForEach(ary, b => Console.WriteLine(BitConverter.ToString(b)));

  Console.WriteLine("=== Method2(参照型の値渡し, メンバー編集) ===");
  Method2(ary);
  Array.ForEach(ary, b => Console.WriteLine(BitConverter.ToString(b)));

  Console.WriteLine("=== Method3(参照型の参照渡し, 参照情報差し替え) ===");
  Method3(ref ary);
  Array.ForEach(ary, b => Console.WriteLine(BitConverter.ToString(b)));

  Console.WriteLine("=== Method4(参照型の値渡し, 参照情報差し替え) ===");
  Method4(ary);
  Array.ForEach(ary, b => Console.WriteLine(BitConverter.ToString(b)));

  Console.WriteLine("=== originalの状態 ===");
  Array.ForEach(original, b => Console.WriteLine(BitConverter.ToString(b)));

  Console.ReadLine();
 }

 static void Method1(ref byte[][] a)
 {
  a[1] = new byte[] { 0xa1, 0xa2, 0xa3 };
 }

 static void Method2(byte[][] a)
 {
  a[2] = new byte[] { 0xfe, 0xff };
 }

 static void Method3(ref byte[][] a)
 {
  a = new byte[][] { new byte[] { 1, 2, 3 } };
 }

 static void Method4(byte[][] a)
 {
  a = new byte[][] { new byte[] { 0x91, 0x92, 0x93 } };
 }

}

/*******************************/

=== 最初の状態 ===
11-12-13
21-22-23-24
31-32-33-34-35
41-42-43-44-45-46
=== Method1(参照型の参照渡し, メンバー編集) ===
11-12-13
A1-A2-A3
31-32-33-34-35
41-42-43-44-45-46
=== Method2(参照型の値渡し, メンバー編集) ===
11-12-13
A1-A2-A3
FE-FF
41-42-43-44-45-46
=== Method3(参照型の参照渡し, 参照情報差し替え) ===
01-02-03
=== Method4(参照型の値渡し, 参照情報差し替え) ===
01-02-03
=== originalの状態 ===
11-12-13
A1-A2-A3
FE-FF
41-42-43-44-45-46
引用返信 編集キー/
■76100 / inTopicNo.4)  Re[2]: 参照型の参照渡し
□投稿者/ ATM (2回)-(2015/06/02(Tue) 01:24:13)
No76090 (Azulean さん) に返信
> 読まれていた記事の「参照型変数の参照渡し」は ReferenceMethod のように「ref キーワードを使うこと」なのでは?

レスありがとうございます。
そうなります。魔界の仮面弁士 さんのサンプルコードでいうとMethod3になります。
引用返信 編集キー/
■76101 / inTopicNo.5)  Re[2]: 参照型の参照渡し
□投稿者/ ATM (3回)-(2015/06/02(Tue) 02:06:40)
No76092 (魔界の仮面弁士 さん) に返信
サンプルコードまでご用意いただきありがとうございます。

> それは、どこにあった解説でしょうか?
情報元
http://smdn.jp/programming/netfx/valuetype_referencetype/
【値型・参照型の挙動の違い】−[値渡し・参照渡し]
メソッドの引数を参照渡し(ref/ByRef)する場合は、値型の場合でも引数に指定したインスタンスを参照することになるため、メソッド内で引数に変更を加えると参照型の場合と同様に呼び出し元の変数に反映されます。
以上引用

当方で引数を参照型の参照情報と言い替えて記述しています。

>>参照型の参照渡しをした時メソッド側で参照型の参照情報を変更すると呼び出し元の参照情報も変更される
>
> 上記の文章は、下記の Method1〜Method4 のうち、いずれの動作を指しているのでしょうか。
Method3になります。

>参照型の引数はスタックメモリ領域のメモリ番地(aaaa)を格納している ← ここが特に知りたい
上記はMethod3でいうと引数 a のことです。引数 a には何が格納されているのかとということが知りたいのです。
また a をいじるとメモリ領域の何処にある何が影響を受けるのかということが知りたいです。
引用返信 編集キー/
■76102 / inTopicNo.6)  Re[3]: 参照型の参照渡し
□投稿者/ 774RR (264回)-(2015/06/02(Tue) 09:39:56)
2015/06/02(Tue) 09:40:31 編集(投稿者)
ATM 氏は C (のポインタ) がわかるのだろうか?わかるようなら C/C++ で書いちゃうけど・・

static void Main() {
  byte* ary=new byte[X]; // ary はポインタ(参照) ary 自体はスタック、その指す先はヒープ
  Method1(&ary); // Method1 は「ポインタへのポインタ」を渡している(実質的参照渡し)
  Method2(ary); // ポインタ値を複写している(値渡し)
  Method3(&ary); // ditto
  Method4(ary); // ditto
}

void Method4(byte* a) {
  // a は元の ary の値の複写
  // a 自体はスタックにあるが、その指す先がどこかは気にしなくていい
  a=new byte[XX]; // a を上書きしても ary は変化しない
  // 以下で使われる a は ary とは別のもの
}

void Method3(byte** a) {
  // a は元の ary へのポインタ(参照)
  // a 自体はスタックにあるが、その指す先がどこかは気にしなくていい
  *a=new byte[ZZ]; // a (の指す先)を上書きするので ary も変化する
  // 以下で使われる *a は ary と同じもの
}


引用返信 編集キー/
■76110 / inTopicNo.7)  Re[3]: 参照型の参照渡し
□投稿者/ 魔界の仮面弁士 (360回)-(2015/06/02(Tue) 13:49:56)
No76101 (ATM さん) に返信
> メソッドの引数を参照渡し(ref/ByRef)する場合は、値型の場合でも引数に指定したインスタンスを参照することになるため、メソッド内で引数に変更を加えると参照型の場合と同様に呼び出し元の変数に反映されます。

その文章は Method3 の動作ではなく、Method1 相当の説明ではありませんか?


smdn のサンプルでは、『引数に変更を加える』例として
 static void Method(ref ValType v)
 {
   v.ID = 2;
 }
 static void Method(ref RefType r)
 {
   r.ID = 2;
 }
と書かれていますが、これは先の Method1 にあたる動作です。


Method3 相当の説明だとすれば、サンプルコードは
 static void Method3(ref ValType v)
 {
   v = new ValType(3);
 }
 static void Method3(ref RefType r)
 {
   r = new RefType(3);
 }
となるべきかと。


でもって上記 Method と Method3 は、呼びだし元で
  var R = r;
  var V = v;
  Method(ref r);
  Method3(ref r);
  Method(ref v);
  Method3(ref v);
とした場合、
  r.ID は 3
  R.ID は 2
  v.ID は 3
  V.ID は 1
という結果になりますね。
引用返信 編集キー/
■76126 / inTopicNo.8)  Re[4]: 参照型の参照渡し
□投稿者/ 774RR (265回)-(2015/06/02(Tue) 17:41:12)
76110 の解説は「値型を参照渡ししたら」ということで C/C++ なら同様

void Main() {
  valuetype v=0;
  value_call_method(v); // 値型を値渡し:関数の実装によらず v は変化しない(しえない)
  ref_call_methodA(&v); // 値型を参照渡し:関数が v を変更していないから値は変わらない
  ref_call_methodB(&v); // 値型を参照渡し:関数が v を変更しているので値が変わる
  ref_call_methodC(&v); // 値型を参照渡し:参照を変更しても元の値は変わらない
}

void value_call_method(valuetype a) {
  ++a; // 複写した値を変更しているので元の値は変化しない
}

void ref_call_methodA(valuetype* a) {
  // 値型への参照を渡しても、別に変更しなきゃならない必然はない
  // C/C++ ではこういうとき valuetype const * と明示することができる
}

void ref_call_methodB(valuetype* a) {
  ++(*a); // 参照を経由して元の値を変更している
  // 元の値を変更しているので valuetype const * と書けない
}

void ref_call_methodC(valuetype* a) {
  valuetype b=1;
  a=&b; // 参照する先を入れ替えても「もともと参照されていた実体」は変わらない
}


引用返信 編集キー/
■76131 / inTopicNo.9)  Re[4]: 参照型の参照渡し
□投稿者/ ATM (4回)-(2015/06/02(Tue) 23:53:17)
No76110 (魔界の仮面弁士 さん) に返信
> その文章は Method3 の動作ではなく、Method1 相当の説明ではありませんか?
「参照型の場合と同様に」との文言から引数が参照型でも同様にそうであると解釈してしまっているのですが、どうもその辺がそもそも曲解というか無理やりすぎたのかとも思います。

具体的に結果を数字を例に挙げてくださりありがとうございます。
おかげさまで整理がついてきました。

引用返信 編集キー/
■76132 / inTopicNo.10)  Re[4]: 参照型の参照渡し
□投稿者/ ATM (5回)-(2015/06/02(Tue) 23:59:23)
No76102 (774RR さん) に返信
> ATM 氏は C (のポインタ) がわかるのだろうか?わかるようなら C/C++ で書いちゃうけど・・
C/C++は使ったことありませんがポインタがどういったものかは少し分かります。

解説の仕方が私のしてほしかったことにピッタリです。
丁寧な解説ありがとうございました。

これらの情報で一度考え直してみますので解決とさせてもらいます。
みなさまありがとうございました。
解決済み
引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -