参照渡しと値渡しの違いは何ですか?


回答:


1079

何よりまず、CS理論で定義されていた「値渡しと参照渡し」の区別は、元々「参照渡し」として定義されていた手法が支持されなくなったため、現在はほとんど使用されていないため廃止されました1

新しい言語2は、異なる(ただし類似した)技術のペアを使用して、混乱の主な原因である同じ効果(以下を参照)を達成する傾向があります。

「参照渡し」では、「参照」は一般的な用語「参照」よりも狭い意味を持っているという事実があります(そのフレーズがそれより前にあるため)。


さて、本物の定義は:

  • パラメーターが参照渡しされる場合、呼び出し元と呼び出し先はパラメーターに同じ変数使用します。呼び出し先がパラメータ変数を変更した場合、その効果は呼び出し元の変数に表示されます。

  • パラメータが値によって渡される場合、呼び出し元と呼び出し先には、同じ値を持つ2つの独立した変数があります。呼び出し先がパラメータ変数を変更した場合、その効果は呼び出し元には見えません。

この定義で注意すべき点は次のとおりです。

  • ここでの「変数」とは、呼び出し元の(ローカルまたはグローバル)変数自体を意味します。つまり、ローカル変数を参照で渡し、それに割り当てた場合、呼び出し元の変数自体を変更します。たとえば、ポインターの場合、それが指しているものは変更しません。 。

    • これは現在、(暗黙的な依存関係として)悪い習慣と見なされています。そのため、事実上すべての新しい言語は、排他的に、またはほとんど排他的に値渡しです。参照渡しは、関数が複数の値を返すことができない言語では、主に「出力/入出力引数」の形式で使用されます。
  • 「参照渡し」における「参照」の意味。一般的な「参照」という用語との違いは、この「参照」は一時的で暗黙的なものであることです。呼び出し先が基本的に取得するのは、元の変数と何とか「同じ」「変数」です。この効果が具体的にどのように達成されるかは関係ありません(たとえば、言語は実装の詳細(アドレス、ポインター、逆参照)を公開する場合もあります。これはすべて関係ありません。これが実質的な影響である場合、参照渡しです)。


現在、現代の言語では、変数は「参照型」「参照渡し」より後に発明され、それから着想を得たもの)になる傾向があります。つまり、実際のオブジェクトデータはどこかに(通常はヒープ上に)個別に格納されます。それへの「参照」だけが変数に保持され、パラメータとして渡されます。

変数の値は技術的には参照自体であり、参照されるオブジェクトではないため、このような参照を渡すことは値渡しに該当します。ただし、プログラムへの最終的な影響は、値渡しまたは参照渡しと同じです。

  • 参照が呼び出し元の変数から取得され、引数として渡される場合、これは参照渡しと同じ効果があります。参照先のオブジェクトが呼び出し先で変更れた場合、呼び出し元は変更を確認します。
    • ただし、この参照を保持している変数が再度割り当てられると、そのオブジェクトへのポイントが停止するため、この変数に対する以降の操作は、現在ポイントしているものに影響を与えます。
  • 値渡しと同じ効果を得るには、ある時点でオブジェクトのコピーを作成します。オプションは次のとおりです。
    • 呼び出し元は、呼び出しの前にプライベートコピーを作成し、代わりに呼び出し先にその参照を与えることができます。
    • 一部の言語では、一部のオブジェクトタイプは「不変」です。値を変更するように見えるオブジェクトに対する操作では、元のオブジェクトに影響を与えずに、まったく新しいオブジェクトが実際に作成されます。したがって、そのようなタイプのオブジェクトを引数として渡すと、常に値渡しの効果があります。変更が必要な場合は、呼び出し先のコピーが自動的に作成され、呼び出し元のオブジェクトは影響を受けません。
      • 関数型言語では、すべてのオブジェクトは不変です。

ご覧のとおり、この2つの手法は定義の手法とほとんど同じですが、間接参照のレベルのみが異なります。「変数」を「参照オブジェクト」に置き換えるだけです。

それらには合意された名前はありません。これは、「値が参照である場合の値による呼び出し」などの説明が歪む原因になります。1975年に、Barbara Liskovは「オブジェクト共有による呼び出し」(または単に「呼び出しによる共有」という場合もある)という用語を提案しました。さらに、これらのフレーズはどちらも元のペアとの類似点を示していません。古い用語が何もない状態で再利用されて混乱に終わったのも不思議ではありません。4


:長い間、この答えは次のように言っていました。

あなたとウェブページを共有したいとします。URLを教えていただければ、参照で渡します。そのURLを使用して、私と同じWebページを表示できます。そのページが変更された場合、両方の変更が表示されます。URLを削除すると、そのページへの参照が破棄されるだけで、実際のページ自体は削除されません。

ページを印刷して印刷すると、値渡しになります。あなたのページはオリジナルの切り離されたコピーです。その後の変更は表示されず、加えた変更(印刷物の落書きなど)は元のページには表示されません。プリントアウトを破棄すると、実際にはオブジェクトのコピーが破棄されますが、元のWebページはそのまま残ります。

これは、「参照」のより狭い意味を除いほとんど正しいです-一時的および暗黙的の両方です(必須ではありませんが、明示的および/または永続的であることが追加機能であり、参照渡しセマンティクスの一部ではありません) 、上記で説明したように)。よく似ているのは、ドキュメントのコピーを提供するのではなく、オリジナルに取り組むように招待するのです。


1 FortranまたはVisual Basicでプログラミングしている場合を除き、これはデフォルトの動作ではありません。また、最近のほとんどの言語では、真の参照渡しは不可能です。

2 古いものもかなりサポートしています

3 いくつかの現代の言語では、すべての型が参照型です。このアプローチは、1975年にCLU言語によって開拓され、それ以降、PythonやRubyなどの他の多くの言語で採用されています。さらに、より多くの言語がハイブリッドアプローチを使用しており、一部のタイプは「値タイプ」であり、その他のタイプは「参照タイプ」です。その中にはC#、Java、JavaScriptがあります。

4 適切な古い用語自体をリサイクルしても何も悪いことはありませんが、毎回どの意味が使用されているかを何らかの方法で明確にする必要があります。そうしないと、混乱を引き起こし続けます。


私は個人的に、「新しい」または「間接的な」値渡し/参照渡しの用語を新しい手法に使用します。
ivan_pozdeev

あなたが提供する「本物の」定義は、ほとんどすべての入門プログラミングコースで与えられた定義ではありません。参照渡しの内容をGoogleに通知すると、その回答は得られません。あなたが提供する本物の定義は、参照という言葉の誤用です。その定義に従うとき、参照ではなくエイリアスを使用しているためです。実際には同じ変数である2つの変数、つまりエイリアスであり、参照ではありません。あなたの本物の定義は、理由もなく大混乱を引き起こします。参照渡しとは、アドレスを渡すことを意味します。それは理にかなっており、この無意味な混乱を回避します。
YungGun

@YungGun 1)「ほとんどすべての入門プログラミングコースで与えられた定義」へのリンクを提供してください。また、これは、CSコースが作成された10〜3年前の現実ではなく、今日の現実を明確にすることを目的としています。2)「アドレス」は、可能な実装から意図的に抽象化されているため、定義では使用できません。たとえば、一部の言語(Fortran)にはポインターがありません。また、生のアドレスをユーザーに公開するかどうかも異なります(VBは公開しません)。また、生のメモリアドレスである必要はありません。変数にリンクできるものであれば何でも可能です。
ivan_pozdeev

@ivan_podeevリンクはありません。私が「ほとんどすべての入門コース」と言ったのは、私が個人的に大学に行き、プログラミングのブートキャンプも受講したためです。これらのコースはモダンでした(5年未満)。「生のアドレス」は「ポインタ」の同義語です...あなたは技術的には正しいかもしれませんが(チェリーが選んだリンクによると)、使用している言語は実用的ではなく、ほとんどのプログラマにとって混乱しています。あなたはそれに私の完全な考えをしたい場合、私は3500語ブログ記事を書いた:medium.com/@isaaccway228/...を
YungGun

@YungGun「長すぎて、読みませんでした」。一見すると、回答で概説されている混乱が正確にわかります。参照渡しは、実装に依存しない抽象的な手法です。フードの下で正確に何が渡されるかは関係ありません。プログラムへの影響がどの程度かは関係ありません。
ivan_pozdeev

150

関数に引数を渡す方法です。参照渡しとは、呼び出された関数のパラメーターが呼び出し元の渡された引数と同じであることを意味します(値ではなくID-変数自体)。値渡しは、呼び出された関数のパラメーターが呼び出し元の渡された引数のコピーになることを意味します。値は同じですが、ID(変数)は異なります。したがって、呼び出された関数によって行われるパラメータを変更すると、渡された引数が変更され、呼び出された関数(コピーのみ)のパラメータの値が変更されるだけです。急いで:

  • Javaは値渡しのみをサポートします。オブジェクトへの参照をコピーする場合、呼び出された関数のパラメーターは同じオブジェクトを指し、そのオブジェクトへの変更は呼び出し元に表示されますが、常に引数をコピーします。これは混乱を招く可能性があるため、ここでJon Skeetがこれについて述べなければならないことがあります。
  • C#は、値渡しと参照渡し(ref呼び出し元と呼び出し先の関数で使用されるキーワード)をサポートしています。ジョン・スキートも、これについて良い説明をしています
  • C ++は、値渡しと参照渡し(呼び出された関数で使用される参照パラメーター型)をサポートしています。これについては以下で説明します。

コード

私の言語はC ++なので、ここで使用します

// passes a pointer (called reference in java) to an integer
void call_by_value(int *p) { // :1
    p = NULL;
}

// passes an integer
void call_by_value(int p) { // :2
    p = 42;
}

// passes an integer by reference
void call_by_reference(int & p) { // :3
    p = 42;
}

// this is the java style of passing references. NULL is called "null" there.
void call_by_value_special(int *p) { // :4
    *p = 10; // changes what p points to ("what p references" in java)
    // only changes the value of the parameter, but *not* of 
    // the argument passed by the caller. thus it's pass-by-value:
    p = NULL;
}

int main() {
    int value = 10;
    int * pointer = &value;

    call_by_value(pointer); // :1
    assert(pointer == &value); // pointer was copied

    call_by_value(value); // :2
    assert(value == 10); // value was copied

    call_by_reference(value); // :3
    assert(value == 42); // value was passed by reference

    call_by_value_special(pointer); // :4
    // pointer was copied but what pointer references was changed.
    assert(value == 10 && pointer == &value);
}

また、Javaの例は問題ありません。

class Example {
    int value = 0;

    // similar to :4 case in the c++ example
    static void accept_reference(Example e) { // :1
        e.value++; // will change the referenced object
        e = null; // will only change the parameter
    }

    // similar to the :2 case in the c++ example
    static void accept_primitive(int v) { // :2
        v++; // will only change the parameter
    }        

    public static void main(String... args) {
        int value = 0;
        Example ref = new Example(); // reference

        // note what we pass is the reference, not the object. we can't 
        // pass objects. The reference is copied (pass-by-value).
        accept_reference(ref); // :1
        assert ref != null && ref.value == 1;

        // the primitive int variable is copied
        accept_primitive(value); // :2
        assert value == 0;
    }
}

ウィキペディア

http://en.wikipedia.org/wiki/Pass_by_reference#Call_by_value

http://en.wikipedia.org/wiki/Pass_by_reference#Call_by_reference

この男はそれをかなり釘付けにします:

http://javadude.com/articles/passbyvalue.htm


9
なぜ反対票?何かが間違っていたり誤解を招いたりした場合は、コメントを残してください。
Johannes Schaub-litb 2008

1
私の投票ではありませんでしたが、マイナーな点(大統領の議論で取り上げられている種類)については、「戦略」というより「戦術」のほうがいいと思います。
harpo

28
完全性の+1。反対投票について心配しないでください-人々は奇妙な理由でそれをします。電卓に関する意見の質問では、プログラマは電卓を使用するべきではないと思っていた男から、誰もが反対票を投じられました。とにかく、あなたの答えはとても良かったと思いました。
マークブリッティンガム

1
スキートの説明へのリンクは壊れています。
お金指向のプログラマー

スキートの説明へのリンクはまだ壊れています。
Rokit、

85

ここでの多くの回答(特に、最も賛成された回答)は、「参照による呼び出し」が実際に何を意味するのかを誤解しているため、実際には正しくありません。ここに、問題を正直にしようとする私の試みがあります。

TL; DR

最も簡単な言葉で:

  • 値による呼び出しは、関数の引数としてを渡すことを意味します
  • 参照による呼び出しは、変数を関数の引数として渡すことを意味します

比喩的に言えば:

  • 価値による呼び出しは、私が紙に何かを書き留めてあなたに渡す場所です。多分それはURLかもしれないし、多分それは戦争と平和の完全なコピーかもしれない。それが何であれ、私があなたに与えた紙の上にあるので、今では事実上あなたの紙です。これで、その紙を自由に落書きしたり、その紙を使用して別の場所で何かを見つけたり、それをいじったりすることができます。
  • 参照による呼び出しは、何かが書き留められているノートブックをあなたに渡すときです。あなたは私のノートに落書きをするかもしれません(たぶん私はあなたにそうしたいかもしれませんが、私はそうしないかもしれません)。また、あなたまたは私が書いたものが他のどこかに何かを見つける方法に関する情報がある場合、あなたまたは私はそこに行ってその情報をいじることができます。

「値渡し」および「参照渡し」意味しないこと

これらの概念は両方とも、参照型の概念(Javaではのサブタイプであるすべての型Objectであり、C#ではすべてのclass型です)またはCのようなポインター型の概念(意味的に同等)から完全に独立しており、直交していることに注意してくださいJavaの「参照型」に、異なる構文で)。

参照型の概念はURLに対応します。それ自体が情報の一部であり、他の情報への参照ポインタ)でもあります。URLの多くのコピーをさまざまな場所に置くことができ、リンク先のWebサイトは変更されません。Webサイトが更新された場合でも、すべてのURLコピーは更新された情報につながります。逆に、いずれか1つの場所でURLを変更しても、URLの他の書かれたコピーには影響しません。

C ++は、「参照」(例えばの概念があることに注意してくださいint&)ではない JavaとC#の"参照型"のような、しかしである『参照による呼び出し』のように。JavaとC#の「参照型」、およびPythonのすべての型は、CおよびC ++が「ポインタ型」と呼ぶもの(たとえばint*)に似ています。


わかりました、これはより長くより正式な説明です。

用語

まず、いくつかの重要な用語を強調し、私の答えを明確にし、単語を使用するときに全員が同じアイデアを参照していることを確認します。(実際には、これらのトピックに関する混乱の大部分は、意図された意味を完全に伝えない方法で単語を使用することに起因すると考えています。)

まず、C言語のような言語での関数宣言の例を次に示します。

void foo(int param) {  // line 1
  param += 1;
}

そして、これはこの関数を呼び出す例です:

void bar() {
  int arg = 1;  // line 2
  foo(arg);     // line 3
}

この例を使用して、いくつかの重要な用語を定義します。

  • foo1行目で宣言された関数です(Javaはすべての関数をメソッドにすることを主張していますが、概念は一般性を失うことなく同じです; CとC ++はここでは触れない宣言と定義を区別します)
  • paramある仮パラメータfooもライン上で宣言し、1
  • argある変数、具体的にはローカル変数関数のbar宣言と行で初期化は、2
  • argまた、ある引数の特定のへの呼び出しfoo行に3

ここで区別する2つの非常に重要な概念のセットがあります。1つ目は、変数の比較です

  • 値がある式の評価結果の言語で。たとえば、bar上記の関数では、行のint arg = 1;後の式arg があります1
  • 変数がある値のコンテナ。変数は、可変(ほとんどのCライクな言語ではデフォルトです)、読み取り専用(Java finalやC#を使用して宣言されるなどreadonly)、または非常に不変(C ++を使用する場合などconst)にすることができます。

区別するための他の重要な概念のペアは、パラメータ引数です。

  • パラメータ(とも呼ばれる仮パラメータは)ある変数関数を呼び出すときに、呼び出し元によって供給されなければなりません。
  • 引数がある関数の呼び出し元によって供給され、その関数の特定の仮パラメータを満たすために

値による呼び出し

値によって呼び出し、関数の仮パラメータは、新たに関数呼び出しのために作成されており、どれがで初期化されている変数です、引数のは。

これは、他の種類の変数が値で初期化されるのとまったく同じように機能します。例えば:

int arg = 1;
int another_variable = arg;

ここarganother_variable、完全に独立した変数です-それらの値は互いに独立して変更できます。ただし、another_variableが宣言された時点で、と同じ値をarg保持するように初期化され1ます。

これらは独立した変数であるため、変更によるanother_variable影響はありませんarg

int arg = 1;
int another_variable = arg;
another_variable = 2;

assert arg == 1; // true
assert another_variable == 2; // true

これは、上記の例argとの関係とまったく同じparamです。対称性についてここで繰り返します。

void foo(int param) {
  param += 1;
}

void bar() {
  int arg = 1;
  foo(arg);
}

これは、コードを次のように記述した場合とまったく同じです。

// entering function "bar" here
int arg = 1;
// entering function "foo" here
int param = arg;
param += 1;
// exiting function "foo" here
// exiting function "bar" here

つまり、値による呼び出しが何を意味するかを定義する特性は、呼び出し先(fooこの場合)はを引数として受け取りますが、呼び出し元の変数(これらの場合)からのそれらの値に対する独自の変数を持っているということですbar

上記の比喩に戻って、私barとあなたがそうfooである場合、私があなたを呼ぶとき、私はあなたにその上に価値が書かれた紙を手渡します。あなたはその一枚の紙を呼びますparam。その値は、ノートブック(ローカル変数)で、変数に書き込んだ値のコピーですarg

(余談ですが、ハードウェアとオペレーティングシステムによっては、ある関数を別の関数から呼び出す方法についてさまざまな呼び出し規約があります。この呼び出し規約は、私が紙に値を書き込んでそれをあなたに渡すかどうかを決定するようなものです、または私が書いた紙がある場合、または私たち2人の前の壁に書いた場合。これも興味深い主題ですが、この長い回答の範囲をはるかに超えています。)

参照で呼び出す

参照することにより呼び出し、関数の仮パラメータは、単純にある新しい名前を引数として呼び出し側用品、同じ変数について。

上記の例に戻ると、これは次と同等です。

// entering function "bar" here
int arg = 1;
// entering function "foo" here
// aha! I note that "param" is just another name for "arg"
arg /* param */ += 1;
// exiting function "foo" here
// exiting function "bar" here

以来paramのためのちょうど別の名前であるarg-である、彼らは同じ変数に対する変更paramに反映されていますarg。これは、参照渡しが値渡しと異なる基本的な方法です。

参照による呼び出しをサポートする言語はほとんどありませんが、C ++では次のようにできます。

void foo(int& param) {
  param += 1;
}

void bar() {
  int arg = 1;
  foo(arg);
}

この場合、paramはと同じを持つだけでなく、arg実際に arg(単に別の名前で)なので、インクリメントされているbarことを確認できargます。

これは、Java、JavaScript、C、Objective-C、Python、または今日のほとんどの他の一般的な言語が機能する方法ではないことに注意してください。つまり、これらの言語は参照渡しではなく、値渡しです。

補遺:オブジェクト共有による呼び出し

あなたが持っているものが値による呼び出しであるが、実際の値が参照型またはポインタ型である場合、「値」自体はそれほど興味深いものではありません(たとえば、Cではプラットフォーム固有のサイズの単なる整数です)。興味深いのは、その値が指すものです。

その参照型(つまり、ポインター)が指しているものが変更可能である場合、興味深い効果が得られます。つまり、ポイント先の値を変更でき、呼び出し元は監視できなくても、ポイント先の値への変更を監視できます。ポインタ自体に変わります。

再びURLの類推を借りると、私があなたにWebサイトへのURLのコピーを提供したという事実は、私たち2人がURLではなくWebサイトである場合、特に興味深いものではありません。URLのコピーを上書きしても、私のURLのコピーには影響しないという事実は、私たちが気にすることではありません(実際、JavaやPythonなどの言語では、「URL」、つまり参照型の値によって、まったく変更しないでください。それによってポイントされたものだけが変更できます)。

Barbara Liskovは、CLUプログラミング言語(これらのセマンティクスを持つ)を発明したとき、既存の用語「値による呼び出し」および「参照による呼び出し」は、この新しい言語のセマンティクスを説明するのに特に有用ではないことに気付きました。そこで、彼女は新しい用語を考案しました。オブジェクト共有による呼び出しです。

技術的に値で呼び出されているが、一般的に使用されている型が参照型またはポインタ型(つまり、ほとんどすべての現代の命令型、オブジェクト指向型、またはマルチパラダイムプログラミング言語)である言語について説明するときは、混乱が少ないことがわかります。による呼び出し参照による呼び出しについて話すのは避けてください。スティックするオブジェクト共有によって呼び出す(あるいは単にオブジェクトで呼び出す)と誰もが混同されません。:-)


より良い説明:ここで区別するために2つの非常に重要な概念のセットがあります。 The first is value versus variable. The other important pair of concepts to distinguish is parameter versus argument:
SK Venkat

2
すばらしい答えです。参照渡しで新しいストレージを作成する必要がないことを付け加えたいと思います。パラメータ名は元のストレージ(メモリ)を参照します。
ありがとう

1
ベストアンサーIMO
Rafael Eyng

59

2つの用語を理解する前に、次のことを理解する必要があります。すべてのオブジェクトには、それを区別できる2つのものが含まれています。

  • その価値。
  • そのアドレス。

だからあなたが言うなら employee.name = "John"

について2つのことを知っていますname。その値と"John"、メモリ内の16進数の位置は、次のようになります0x7fd5d258dd00

言語のアーキテクチャまたはオブジェクトのタイプ(クラス、構造体など)に応じて、転送する"John"か、0x7fd5d258dd00

受け渡し"John"は、値渡しと呼ばれます。渡すこと0x7fd5d258dd00は、参照渡しと呼ばれます。このメモリ位置を指している人は誰でもの値にアクセスできます"John"

詳細については、ポインターの逆参照についておよびクラス(参照型)ではなく構造体(値型)を選択する理由をお読みになることをお勧めします。


3
それは私が探していた、実際には人は説明ではなく概念を探すべきです、仲間を高く評価します。
Haisum Usman 2017

Javaは常に値渡しです。Javaでオブジェクトへの参照を渡すことは、値渡しと見なされます。これは、「0x7fd5d258dd00を渡すことは参照渡しと呼ばれます」という文と矛盾します。
chetan raina

53

次に例を示します。

#include <iostream>

void by_val(int arg) { arg += 2; }
void by_ref(int&arg) { arg += 2; }

int main()
{
    int x = 0;
    by_val(x); std::cout << x << std::endl;  // prints 0
    by_ref(x); std::cout << x << std::endl;  // prints 2

    int y = 0;
    by_ref(y); std::cout << y << std::endl;  // prints 2
    by_val(y); std::cout << y << std::endl;  // prints 2
}

1
最後の行には2ではなく0が表示されるため、問題が1つあると思います。何か不足しているものがあったら教えてください。
Taimoor Changaiz 2014年

@TaimoorChangaiz; どの「最後の行」?ちなみにIRCが使えるならFreenodeの## programingに来てください。そこで説明するほうがずっと簡単でしょう。私のニックネームは「pyon」です。
ピョン2014年

1
@EduardoLeónby_val(y); std :: cout << y << std :: endl; // 2を出力
Taimoor Changaiz 2014年

5
@TaimoorChangaiz:なぜ2を印刷しないのですか?y前の行ですでに2に設定されています。なぜ0に戻るのですか?
ピヨン2014年

@EduardoLeón私の悪い。はい、あなたは正しいです。訂正ありがとう
Taimoor Changaiz 14年

28

これを取得する最も簡単な方法は、Excelファイルです。たとえば、セルA1とB1に2つの数値5と2があり、それらの合計を3番目のセルで見つけたいとします。たとえば、A2とします。これには2つの方法があります。

  • このセルに= 5 + 2と入力してセルA2に値を渡すか、この場合、セルA1またはB1の値が変化しても、A2の合計は同じままです。

  • または、= A1 + B1と入力して、セルA1およびB1の「参照」をセルA2に渡します。この場合、セルA1またはB1の値が変化すると、A2の合計も変化します。


これは、他のすべての回答の中で最も単純で最良の例です。
アミット・レイ

18

refを渡すときは、基本的に変数へのポインターを渡します。変数のコピーを渡す値渡し。基本的な使用法では、これは通常、変数へのrefによる変更の受け渡しが、呼び出し側のメソッドであると見なされ、値が受け渡されないことを意味します。


12

値渡しは、指定した変数に格納されているデータのコピーを送信します。参照渡しは、変数自体への直接リンクを送信します。したがって、変数を参照で渡し、それを渡したブロック内の変数を変更すると、元の変数が変更されます。単に値で渡す場合、元の変数は、それを渡したブロックによって変更することはできませんが、呼び出し時に含まれていたもののコピーを取得できます。


7

値渡し-関数は変数をコピーし、コピーを操作します(元の変数の内容は変更されません)

参照渡し-関数は元の変数を使用します。他の関数で変数を変更すると、元の変数も変更されます。

例(コピーして使用/試してみてください):

#include <iostream>

using namespace std;

void funct1(int a){ //pass-by-value
    a = 6; //now "a" is 6 only in funct1, but not in main or anywhere else
}
void funct2(int &a){ //pass-by-reference
    a = 7; //now "a" is 7 both in funct2, main and everywhere else it'll be used
}

int main()
{
    int a = 5;

    funct1(a);
    cout<<endl<<"A is currently "<<a<<endl<<endl; //will output 5
    funct2(a);
    cout<<endl<<"A is currently "<<a<<endl<<endl; //will output 7

    return 0;
}

シンプルにしてください。テキストの壁は悪い習慣になり得ます。


これは、パラメーター値が変更されたかどうかを理解するのに非常に役立ちます。ありがとうございます。
Kevin Zhao

5

それらの主な違いは、値型変数は値を格納するため、メソッド呼び出しで値型変数を指定すると、その変数の値のコピーがメソッドに渡されます。参照型変数はオブジェクトへの参照を格納するため、参照型変数を引数として指定すると、オブジェクトを参照する実際の参照のコピーがメソッドに渡されます。参照自体は値で渡されますが、メソッドは受け取った参照を使用して、元のオブジェクトと対話し、場合によっては変更します。同様に、returnステートメントを介してメソッドから情報を返す場合、メソッドは値型変数に格納されている値のコピー、または参照型変数に格納されている参照のコピーを返します。参照が返されると、呼び出し側のメソッドはその参照を使用して、参照されるオブジェクトと対話できます。そう、

C#では、呼び出されたメソッドが変数を変更できるように参照によって変数を渡すために、C#はキーワードrefおよびoutを提供します。パラメータ宣言にrefキーワードを適用すると、参照によってメソッドに変数を渡すことができます。呼び出されたメソッドは、呼び出し元の元の変数を変更できます。refキーワードは、呼び出しメソッドですでに初期化されている変数に使用されます。通常、メソッド呼び出しに初期化されていない変数が引数として含まれている場合、コンパイラーはエラーを生成します。パラメータの前にキーワードoutを指定すると、出力パラメータが作成されます。これは、引数が参照によって呼び出されたメソッドに渡され、呼び出されたメソッドが呼び出し元の元の変数に値を割り当てることをコンパイラーに示します。メソッドが実行のすべての可能なパスで出力パラメーターに値を割り当てない場合、コンパイラーはエラーを生成します。これにより、コンパイラが、メソッドに引数として渡される初期化されていない変数のエラーメッセージを生成することも防止されます。メソッドはreturnステートメントを介して呼び出し元に値を1つだけ返すことができますが、複数の出力(refおよび/またはout)パラメータを指定することで多くの値を返すことができます。

C#のディスカッションと例をここにリンクテキストを参照してください


3

例:

class Dog 
{ 
public:
    barkAt( const std::string& pOtherDog ); // const reference
    barkAt( std::string pOtherDog ); // value
};

const &一般的には最高です。建設や破壊のペナルティは発生しません。参照がconstでない場合、インターフェースは、渡されたデータを変更することを示唆しています。


2

要するに、値で渡されるのはそれが何であるかであり、参照で渡されるのはそれがどこであるかです。

値がVAR1 = "Happy Guy!"の場合、 "Happy Guy!"のみが表示されます。VAR1が「Happy Gal!」に変わっても、それはわかりません。参照によって渡され、VAR1が変更された場合は、変更されます。


2

関数に渡した後で元の変数の値を変更したくない場合は、「値渡し」パラメーターを使用して関数を作成する必要があります。

その後、関数は値のみを持ち、渡された変数のアドレスは持ちません。変数のアドレスがないと、関数の内側のコードは、関数の外側から見た変数の値を変更できません。

ただし、外部から見た変数の値を変更する機能を関数に与えたい場合は、参照渡しを使用する必要があります。値とアドレス(参照)の両方が渡され、関数内で使用できるためです。


1

値渡しは、引数を使用して関数に値を渡す方法を意味します。値渡しでは、指定した変数に格納されているデータをコピーします。データがコピーされるため、参照渡しよりも遅くなります。コピーされたデータに変更を加えるので、元のデータは影響を受けません。参照渡しまたはアドレス渡しで、変数自体への直接リンクを送信します。または、ポインタを変数に渡します。BCSEの方が高速で、消費される時間も少ない


0

次に、値渡し-ポインタ値-参照の違いを示す例を示します

void swap_by_value(int a, int b){
    int temp;

    temp = a;
    a = b;
    b = temp;
}   
void swap_by_pointer(int *a, int *b){
    int temp;

    temp = *a;
    *a = *b;
    *b = temp;
}    
void swap_by_reference(int &a, int &b){
    int temp;

    temp = a;
    a = b;
    b = temp;
}

int main(void){
    int arg1 = 1, arg2 = 2;

    swap_by_value(arg1, arg2);
    cout << arg1 << " " << arg2 << endl;    //prints 1 2

    swap_by_pointer(&arg1, &arg2);
    cout << arg1 << " " << arg2 << endl;    //prints 2 1

    arg1 = 1;                               //reset values
    arg2 = 2;
    swap_by_reference(arg1, arg2);
    cout << arg1 << " " << arg2 << endl;    //prints 2 1
}

「参照渡し」方式には重要な制限があります。パラメータが参照渡しとして宣言されている場合(そのため&記号が前に付いている)、対応する実際のパラメータは変数である必要があります

「値で渡される」仮パラメーターを参照する実際のパラメーターは、一般にである可能性があるため、変数だけでなく、リテラルまたは関数呼び出しの結果を使用することもできます。

関数は、変数以外の何かに値を置くことができません。リテラルに新しい値を割り当てたり、式で結果を強制的に変更したりすることはできません。

PS:わかりやすい言葉で説明している現在のスレッドでDylan Beattieの回答を確認することもできます。


「パラメータが[参照として]宣言されている場合、対応する実際のパラメータは変数でなければならない」と述べていますが、これは一般的には当てはまりません。参照が一時関数(関数の戻り値など)にバインドされている場合、その寿命は参照と一致するように延長されます。詳細はこちらをご覧ください。
クリスハント
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.