疑問メモ: 値渡し、参照渡し、Java、C

Javaのオブジェクト参照は、Cのポインタをラップしたようなモノである、という理解なのだけれど、要は何なのだろう?

- Java C C++
仮引数が基本データ型(Java) or 変数のみ(C/C++ 値渡し 値渡し 値渡し
仮引数が配列かオブジェクト(Java) or 変数*(C/C++ (オブジェクト参照の)値渡し (ポインタの)値渡し (ポインタの)値渡し
仮引数が変数&(C++ - - 参照渡し

ちょっと整理する。

CとJavaの値渡し

Javaでは、メソッドの引数が基本データ型のとき、実引数の値のコピーが仮引数に渡される。これは、C言語で関数の仮引数に何もつけないときと同じ動きになる。

また、C言語では、関数の仮引数がポインタのときに、ポインタが値渡しされる。これを参照渡しと呼ぶのは誤りである。アドレスがコピーされて渡されるだけなので、値渡しである。

Javaでメソッドの引数がオブジェクトのときも、オブジェクト参照が値渡しされる。これも参照渡しではない。

C++の参照渡し

C++には本物の参照渡しがある。参照渡しでは、実引数のアドレスが関数に渡され、仮引数と同じアドレスに配置される。実引数と仮引数が同じ記憶域を共有する。

  • 関数間の独立性を保ちづらい
  • 再帰を書くのが困難
  • 呼び出し側のコードを見ただけでは、渡しているのが値なのか参照なのか判別できない

とか、いろいろある。

Javaの「オブジェクト参照の値渡し」のサンプル

古い記事にいいカンジのサンプルがあった。

ちょっと改造する。

import java.awt.Point;

public class RefSample {
    public static void tricky(Point arg1, Point arg2) {
        arg1.x = 100;
        arg1.y = 100;
        
        // スワップを試みる
        Point temp = arg1;
        arg1 = arg2;
        arg2 = temp;
        
        // 値に触れることを確認
        arg2.x += 50;
        
        System.out.println("arg1 X: " + arg1.x + " Y: " + arg1.y);
        System.out.println("arg2 X: " + arg2.x + " Y: " + arg2.y);
        System.out.println();
    }

    public static void main(String[] args) {
        Point pnt1 = new Point(0, 0);
        Point pnt2 = new Point(0, 0);
        System.out.println("pnt1 X: " + pnt1.x + " Y: " + pnt1.y);
        System.out.println("pnt2 X: " + pnt2.x + " Y: " + pnt2.y);
        System.out.println();
        
        tricky(pnt1, pnt2);
        
        System.out.println("pnt1 X: " + pnt1.x + " Y:" + pnt1.y);
        System.out.println("pnt2 X: " + pnt2.x + " Y: " + pnt2.y);
    }
}

実行結果。

pnt1 X: 0 Y: 0
pnt2 X: 0 Y: 0

arg1 X: 0 Y: 0
arg2 X: 150 Y: 100

pnt1 X: 150 Y:100
pnt2 X: 0 Y: 0

もし、JavaC++のような参照渡しがあれば、スワップが実現してしまい、最後の出力は次のようになるんかな。

pnt1 X: 0 Y: 0
pnt2 X: 150 Y:100

残った疑問

Javaのオブジェクト参照とは、メモリ上のどのような実体なのか?

オブジェクト参照は、アドレスのように、それ自体を足したり引いたりすることはできません。つまり、アドレスを言語機構でラップしたナニカです。

で、それ、なんなん・・・? というのが、今日の残った疑問だったり。