G-chan Square

じーちゃん/へたっぴの綴る、日々のつれづれとか。
twitterのまとめとゲームネタが中心。2015年からロードバイク/ミニベロ始めました。

じゃ、「参照渡し」ってなんだ?

こんにちわ、じーちゃんです。

前回の[javascript] javascriptの関数で引数に配列を渡すと、それは本当に参照渡しか? のエントリーにて、いおりんからツッコミを受けたので、もうちょっと調べてみました。

つまり「参照渡し」って結局、なんなんだ?ってことで
で、いろいろググってみたりして一番最初に見つけたのがこれ。

JavaHouse Re: 文字列引数の参照渡しにて、かなり「おお、なるほど」と思われる内容が書かれています。
しかも、書いてる人は、あの高木浩光先生だw

とりあえず、内容を一部引用転載させてもらうと
プログラム言語用語であるところの「参照渡し/値渡し」というのは、
正式には「参照による呼び出し/値による呼び出し」と呼ばれるもので、
    何が引数に渡されるか? 値か? 参照か?
    ^^^^
という話ではなく、
    実引数をどのように仮引数に渡すか?
            ^^^^^^^^^^
についての話です。
より明確には、
    実引数が「左辺値」として評価し得る式であるときに、左辺値を渡す
    (call-by-reference)のか、右辺値を渡す(call-by-value)のか
という話です。
「左辺値として評価し得る式」とは、例えば、
    a
    a[i]
などで、そうでないものは、
    1
    1 + 2
などです。(代入文の左辺に書ける式かどうかということ。)
代入文
    a = b;
    a[i] = a[j];
においては、「a」「a[i]」が左辺値、「b」「a[j]」は右辺値です。
参照渡し/値渡しという区別は、
    foo(a);
    foo(a[i]);
において、「a」「a[i]」が左辺値として扱われるか右辺値として扱われるか
のことです。
たとえ、変数「a」や配列の要素「a[i]」が保持する値が「オブジェクトへの
参照」であろうが、
    foo(a);
    foo(a[i]);
のことを、「参照渡し(call-by-reference)」とは呼びません。
「による呼び出し」という言い回しを「渡し」などと略すから誤解を招いて
いるのでしょう。
C言語で書いたときの、
        Foo* f;
        f = ...;
        bar(f);
    ...
    void bar(Foo* foo) {
        ...
        foo = ...;
    }
が、Javaにおける(primitive型以外の)すべての引数の渡し方 (と仮引数への
代入) であり、これは「参照渡し(call by reference)」ではありません。
「参照渡し(call by reference)」(っぽいもの) (と仮引数への代入) をC言語
で書くと、
        Foo* f;
        f = ...;
        bar(&f);
    ...
    void bar(Foo* *foo) {
        ...
        *foo = ...;
    }
で、これはJava言語では記述不可能です。これが不可能なのは、
  ・Java言語は「変数への参照(ポインタ)を取得する」機能を排除した。
ためです。
なお、この上記のC言語による「参照渡し(っぽいもの)」においても、C言語
自体の機能としては「値渡し(call-by-value)」であり、C言語にも「参照渡し
(call-by-reference)」の機能は存在しません。上記の例は、
   参照渡し可能な言語における参照渡しと同等の処理を、値渡しの言語上で、
   変数への参照を得る機能を利用することにより、実現している。
というものです。
なるほど! 参照渡しってのは、仮引数に左辺値が入って初めて成り立つってことなんですな。
じーちゃんも「参照渡し」って言ったら、当然
        Foo* f;
        f = ...;
        bar(&f);
    ...
    void bar(Foo* *foo) {
        ...
        *foo = ...;
    }
のほうだと思っておりました。というか、これで正しいんだろうな。
で、Javaはその機能はないから、前回のエントリで「Javaもメソッドないで配列初期化してその配列の参照をメソッド外に返せる」ってのも間違いですな。
ま、やってみればすぐわかることだったんですがw<できると思い込んでいた


そして、次にこちらの記事。

参照渡し・値渡し - 8時40分が超えられない
つまり、参照という言葉使いが2通りあるのが問題で、「参照の参照渡し」と言った場合、
前の「参照」は、ポインタ(オブジェクトのアドレス)の事であり、後ろの「参照渡し」は
とにかく変数を渡すことです。だから「参照の参照渡し」とは、「ポインタ型変数を
そのまま(シンボルとして)渡すこと」となります。
これは、C言語を知らない人だと「何を言っているのか?」という感じかもしれませんが、C言語がわかると……、
「参照の参照渡し」は下のような感じ。(上のサンプルの使い回しですが)
        Foo* f;
        f = ...;
        bar(&f);
    ...
    void bar(Foo* *foo) {
        ...
        *foo = ...;
    }

「参照の値渡し」は下のような感じ。
        Foo* f;
        f = ...;
        bar(f);
    ...
    void bar(Foo* foo) {
        ...
        foo = ...;
    }
ということですな。

で、関数の引数に値を渡すってのは、実は4つのパターン
1)値の値渡し(プリミティブ型変数の値をそのまま渡す)
2)値の参照渡し(プリミティブ型変数の参照を渡す)
3)参照の値渡し(参照型変数のもつ値を値として渡す)
4)参照の参照渡し(参照型変数自体の参照を渡す)
があって、世の中で広く「参照渡し」と言われているケースが3)と4)が混同されているわけですな。まぁ、確かに、どちらも参照が渡っているには変わりはないと言えば変わりないんですがw

ちなみに、じーちゃん的には「参照渡し」と言えば4)だと思っているし、「AS3は参照渡し(call by reference)ではない。値渡し(call by value)である。AS/JS には参照渡しは無い」と言う人がいるように、混同せずにちゃんと使い分けている人もいるわけですな。

そんなわけで、前回の[javascript] javascriptの関数で引数に配列を渡すと、それは本当に参照渡しか?において、


世間ではこれを「参照渡し」って言うの!?w
→ 人によっては言うらしいです。

って感じかなぅ?

なので、「この言語は参照渡しです」という説明があったとしても、「じゃ、4)参照の参照渡しなんだね」と決めつけてはならないってことですな。


追記)
AS3では関数の引数はすべて値渡し(call by value)であるにも似たような記事がありました。

コメントする