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

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

ただいまの
回答率

90.34%

  • C#

    7692questions

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

  • .NET Framework

    487questions

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

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

解決済

回答 4

投稿

  • 評価
  • クリップ 0
  • VIEW 605

tmtmshohei

score 7

 前提・実現したいこと

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

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

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

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

 該当のソースコード

using System;

class Test
{
    class Hoge{public int x;}

    public static void Main()
    {
        var hoge = new Hoge();
        hoge.x=1;
        var huga = new Hoge();
        huga.x=2;
        Cal(hoge,huga);
        Console.WriteLine(hoge.x);//10
        Console.WriteLine(huga.x);//20
    }

    static void Cal(Hoge a,Hoge b)
    {
        a.x=10;
        b.x=20;
        a=b;
    }

} 

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

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

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 4

checkベストアンサー

+3

こんにちは。

関数呼び出し時に、新たに仮引数a,b分のメモリを確保し、
そこに変数hogeとhugaのアドレスがコピーされる

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

関数呼び出し時に、新たに仮引数a,b分のメモリを確保し、
そこに変数hogeとhugaに入っている(newで生成されたオブジェクトの)アドレスがコピーされる

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

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/07/27 20:00

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

    キャンセル

  • 2018/07/27 21:37

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

    キャンセル

  • 2018/07/27 21:39

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

    キャンセル

  • 2018/07/27 21:43

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

    キャンセル

  • 2018/07/28 13:56

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

    キャンセル

  • 2018/07/28 15:33

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

    キャンセル

+1

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/07/27 17:36

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

    キャンセル

  • 2018/07/27 18:38 編集

    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自身の番地(参照)を渡されているのではないので。

    キャンセル

  • 2018/07/27 19:58

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

    キャンセル

  • 2018/07/27 20: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がどうかはすみませんが存じません)

    キャンセル

  • 2018/07/27 20:28

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

    キャンセル

  • 2018/07/28 13:57

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

    キャンセル

0

        a=b;

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


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

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/07/27 18:10

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

    キャンセル

  • 2018/07/27 18:14

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

    キャンセル

  • 2018/07/27 20:02

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

    キャンセル

  • 2018/07/27 20:28 編集

    > hogeに代入されたインスタンスのアドレス

    です
    hogeに代入されたアドレスが、aに代入されて関数が呼ばれます

    キャンセル

  • 2018/07/28 13:56

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

    キャンセル

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/28 13:58

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

    キャンセル

  • 2018/07/28 14:05 編集

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

    キャンセル

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

  • ただいまの回答率 90.34%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る

  • C#

    7692questions

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

  • .NET Framework

    487questions

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