値渡しと参照渡しと矢切の渡し

C言語の場合

C言語には値渡しとポインタ渡しがあったことを覚えていますか?


void add(int a, int b){
	a += b;
}

void addPointer(int *a, int b){
	*a += b;
}

void main(){
	int a = 5;
	add(a, 3);
	printf("a=%d\n", a);	//値渡し
	
	addPointer(&a, 3);	//ポインタ渡し
	printf("a=%d\n", a);
}
$ ./a.out
a=5 ←がーん
a=8 ←期待通り。わーい
値渡しは、コピーを関数に渡しているのに対して、 ポインタ渡しは、値がある場所を関数に渡しているためこういう動作になるわけです。

JAVAの場合

じゃあ、JAVAはどうなるのか、というと ということになります。
具体的に見てみましょう。
import java.util.Vector;

public class SendTest {
	public static void main(String[] args) {
		Yagiri watashi = new Yagiri();
		
		int prim = 0;
		
		System.out.println("prim="+prim);
		watashi.send1(prim);
		System.out.println("prim="+prim);
		
		Vector vec = new Vector();
		vec.add(0, 0); //vの最初の要素を「0」にする。
		System.out.println("vec="+vec.get(0));
		
		watashi.send2(vec);
		System.out.println("vec="+vec.get(0));
		
		watashi.send3(vec);
		System.out.println("vec="+vec.get(0));
		
	}

}


class Yagiri{
	public void send1(int prim){
		prim = 5;
	}
	
	public void send2(Vector innerVec){
		innerVec.add(0, 5);	//innerVecの内容を変更する
	}
	
	public void send3(Vector innerVec){
		innerVec = new Vector();	//innerVecが指しているものを別のものにしてみる
		innerVec.add(10);
	}
}
$ java SendTest.java
a=0
a=0	//send1によって、プリミティブなintの値は変化しない
vec=0
vec=5	//うまくいった!
vec=5	//失敗
つまり、
  1. プリミティブ型の引数はC/C++と一緒で、値のコピーを渡す
  2. クラス型の引数はC/C++のポインタに対応する値を渡す
ということになります。
とくにわかりにくいのはsend2, send3の違いでしょう。
これは、図1,2を見るとわかりやすいかもしれません。
あるクラスのインスタンスを作ったとき、メモリ空間上にその実体が作られます。(図1)
このとき、「vec」はこの実体を表す名前ではなく、この実体に付加されるラベルとなります。

図1
で、引数として渡されるのは、そのラベル「vec」のコピーです。
関数の中では、ラベルのコピーの名前がinnerVecと変わりますが、vecと同じものなので、 ラベルが指しているのはメモリ空間上に作られた実体そのものです。(図2)

図2
send2ではそのままinnerVecの中身を書き換えているので、メモリ空間上の実体の中身が変化します。

一方、send3では、
	innerVec = new Vector();
によって、ラベルinnerVecが別の実体を指し示すようになってしまっています。(図3)
そのため、innerVecを変更してもvecが指している実体は変化しません。
変化するのは、innerVecが指している実体だからです。

図3
以上が、JAVAの参照渡しの説明です。

課題

文字列二つを結合するが、 もし結合結果が100文字を超えるようならば結合せずにfalseを返す関数を作りたい。
以下のように実装したところ、予想通りに動かなかった。
そこで、ChangeStringクラスを変更することで、 上記を実装するクラスを作成せよ。
ただし、必要ならばmainを変更してもかまわない。
public class SendPractice {
	public static void main(String[] args) {
		String str = "This is a ";
		
		ChangeString change = new ChangeString();
		if(change.add(str, "pen")){
			System.out.println(str);
		}
		else{
			System.err.println("Too long");
		}
		
	}

}

class ChangeString{
	
	/**
	 * 文字列を二つ組み合わせるメソッド
	 * ただし、長さが100以上になる場合は、組み合わせない。
	 * 
	 * @param data 文字列
	 * @param dataToAdd 追加する文字列
	 * @return 文字列が100文字以上になった場合false
	 */
	public boolean add(String data, String dataToAdd){
		if(data.length() + dataToAdd.length()> 100){
			return false;
		}

		data = data+dataToAdd;
		return true;
	}
}
$ java SendPractice
This is a ←がーん

戻る