これは何?
変数ってなに? という話。
値なのか、参照なのか、代入したらどうなるのか、とか。そのあたり。
いくつかの言語の事例
いくつかの言語の事例を書いてみる。
C言語の場合
C 言語の変数は(概念としては)型が付与された一連のメモリである。この文脈ではオブジェクトと言ってもいい。
変数の型が決まれば、バイト数が決まる。
オブジェクトの正体は、構造体かもしれないし、ポインタかもしれない。
「概念としては」と書いたのは、コンパイルの結果その変数自体が消滅したり、いろいろあり得るから。
C言語では、同じ「一連のメモリ」を別の変数が直接指すということはできない。そうしたければポインタを使う。
あるいは union
を使えば異なるメンバが同じ「一連のメモリ」を指すことが出来る。
Pascal なんかも同じだと思う。
a=b;
という代入文は、暗黙の型変換が入らない場合「b が指す一連のメモリを a が指す一連のメモリにコピーする」という意味になる。
つまり、代入が行われても依然として a は b と別のもの(アドレスが異なる)であり続けるが、値としては同じものになる。
C++ の場合
C++ は「参照」とよばれる種類の変数を作ることが出来る。
「参照」は別の変数が指しているのと同じオブジェクト(この文脈では、型が決まっている一連のメモリ)を指しているかもしれない。
それでも、変数の型が決まればその変数が指しているオブジェクトのバイト数は確定するし、「参照」と呼ばれる種類の変数だからといって指している先が参照という種類のオブジェクトだったりはしない。
C++ の場合
a=b;
という代入文は演算子のオーバーロードがあるので「b が指す一連のメモリを a が指す一連のメモリにコピーする」という意味になるとは限らない。たとえば std::string
はそうなっていない。
代入が行われても依然として a は b と別のもの(アドレスが異なる)であり続けるが、値としては同じものになる、という点は C言語の代入文と同じで、std::string
はそうなっている。
ruby・Python の場合
ruby・Python の変数はすべて「参照」という趣旨の値である。
と思っておけば普通は困らない。
「参照」という趣旨の値はオブジェクトを指している。
複数の変数が同じオブジェクトを指すことは普通にある。
C言語と異なり、ここでいう「オブジェクト」を「参照」を経由せずに変数で示すことはできない(と思っておけば普通は困らない)
C言語でいうと「ポインタしか無い」という状態と似ている。
とはいえ「と思っておけば普通は困らない」と書いたとおり、これは不正確である。
小さい整数 の場合など、そこには参照っぽい値があるものの、私の理解が正しければ(自信ない)参照先のオブジェクトは存在しないという意味でそれは参照ではない。
こういうことになっているのは「immutable な値を保持している」と「immutable な値への参照を保持している」はユーザーから見たら実用上区別できないから。
すべての変数は参照(だと思って良いもの)を代表しているので、代入文
a = b
の結果、 a と b は同じオブジェクトへの参照(だと思って良いもの)となる。小さな整数でも巨大なクラスのインスタンスでも、この点に差はない。
Python の復号代入演算子
Python は a
が mutable なオブジェクトを指している場合、 a = a + b
と a += b
の動作が全く異なるという気持ち悪い仕様(個人の感想です)となっている。
詳しくはこちらに記事を書いている
in-place になるか否かはプリミティブ型のオブジェクト(そんなものは Python にはないと思う)か否かではなく、mutable か否かによる。mutable の場合、新たなオブジェクトの生成をサボって直接上書きするが、immutable の場合は上書きしようがないので in-place にはできない。
Java の場合
Java のことはよく知らないので自信がないけど一応書いておくと。
Java の変数は、プリミティブ型の場合は C言語でいうところの普通の変数と同じような感じになっている。そこには値がある。
参照型(っていうのかな)の変数の場合は、変数が指す先には「参照」という趣旨の値が入っている。この点は ruby や Python と同じ。C++ の参照型変数はオブジェクト(C++ の用語としてのオブジェクト)への参照だが、Java の参照型を指す変数は、参照という趣旨の値が入っている。ここは決定的に異なる。
C/C++ と違って、Java は整数値のアドレスを取るとることができないのでユーザーの体験としては「参照ではない値」と「immutable な値への参照」を区別する必要がない。
なので、ユーザーの感覚としては、プリミティブ型は immutable な値への参照と見なしてもほとんど困らない。困るとすれば、Java 以外の言語(C言語とか)で書かれたライブラリとのやり取りを実装するときぐらいだと思う。どうだろう。
C# のことはもっと知らないんだけど、この観点では Java とほぼ同じじゃないかと思う。
まとめ
C / Pascal のような言語は、変数はそれぞれ一連のメモリを指している感じ。そしてメモリにどんな意味があるかは変数の型でわかる。
C++ は、変数は一連のメモリを指している。std::string
の例で分かる通り、代入文は単なるコピーではない。しかし、実装に立ち入らない人にとっては単なるコピーであるような気分になれるように実装してある。
Ruby / Python / JavaScript なんかは、変数の値はオブジェクトの参照だと思うことができる。オブジェクトには型情報がついている。代入文によって、左辺は右辺と同じオブジェクトを指すようになる。
Java は、参照型とプリミティブ型があり、プリミティブ型の変数は参照ではなく値を保持している。
とはいえ、プリミティブ型の変数が「immutable な値への参照」を保持していると思っても困る場面はほとんどないので、すべての変数が参照を保持していると思ってもほとんど困らない。