13

変数を関数に渡すとき、関数が変数のコピー/複製のみを取得するのはなぜですか?

int n=1;

void foo(int i)
{
    i++;
}

ご存知のように、関数 foo() はnfoo(n) を使用して の値を変更することはできません。

もちろん、変数のアドレスを渡して、パラメーター変数に何らかの変更を加えることができます。
でも、ちょっと不便だと思いませんか?

なぜ c/c++ は、「実際の」変数自体を関数に直接与えるのではなく、関数に複製のみを与えるように設計されているのですか?
このパラダイムの長所/利点は何ですか?


更新:
@paxdiablo の回答を読みました。彼の「カプセル化、モジュール性、効果の局所化」の説明は良いと思います。
しかし、私のやり方では、パラメーター引数の値も保持できます。カプセル化も実現できます。このようにして:(関数がデフォルトで重複する代わりに「実際の」変数を直接取得できると仮定します)

void foo(int n)
{
    int temp=n;
    //Do something to temp...

}

そして私のやり方では、渡されたパラメーターの値を変更したい場合、 「参照渡し」やポインターなどの複雑なメカニズムを排除できます。それが利点です。

しばらく考えた後。私が提案したように c/c++ が設計されていない理由は、単に私のやり方の不便さのためであることに気付きました!
私のやり方では、関数に変数の長いリストがある場合、それはひどいことになります。私が考えるより便利な方法は、実際には不便です。次
のように書く必要があります。

void foo(int a,int b,double c,float d,char s...)
{
    int temp1=a;
    int temp2=b;
    double temp3=c;
    float temp4=d;
    char temp5=s;
    ...
    //Do something to temp{1,2,3,4,5....}

}

そのため、c/c++ の設計者は、利便性とトレードオフのために複雑なメカニズムを導入しています。
私は正しいですか?

4

9 に答える 9

11

この問題については、基本的に 2 つの考え方があります。

1 つ目は値渡しで、呼び出された関数に対して値のコピーが作成されます。

2 つ目は、呼び出された関数に表示されるパラメーターが元の "エイリアス" である参照渡しです。つまり、変更内容がオリジナルに反映されます。

C は通常、値渡し言語です。変数のアドレスを渡し、それを使用して元のアドレスを変更することで、参照渡しをエミュレートできます。

void setTo42 (int *x) { *x = 42; }
:
int y;
setTo42 (&y);
// y is now 42

しかし、それは変数自体を参照で渡すよりも、変数へのポインターを値で渡すことです。

C++ には真の参照型があります。おそらく、C ポインターに問題を抱えている人が非常に多いためです :-) それらは次のように行われます。

void setTo42 (int &x) { x = 42; }

:
int y;
setTo42 (y);
// y is now 42

関数が「外の世界」に与える影響を制限するため、通常は値渡しが推奨されます。通常、効果のカプセル化、モジュール性、および局所化は良いことです。

渡されたパラメーターを任意に変更できることは、モジュール性とコード管理の点で、グローバル変数とほぼ同じくらい悪いことです。

ただし、渡された変数の 1 つを変更することが理にかなっている可能性があるため、参照渡しが必要になる場合があります。

于 2012-07-31T08:53:12.073 に答える
5

最近のほとんどの言語は、値渡しを使用するように定義されています。理由は簡単です。関数がローカル状態を変更できないことがわかっている場合、コードに関する推論が大幅に簡素化されます。関数でローカル状態を変更できるようにする場合は、常に非 const 参照で渡すことができますが、そのようなケースは非常にまれです

更新された質問に応答するために編集されました:

いいえ、あなたは正しくありません。値渡しは、パラメーターを渡すための最も単純なメカニズムです。参照渡しまたはコピーイン/コピーアウトはより複雑です (もちろん、Algol の式の置換が最も複雑です)。

しばらく考えてみてください。を考慮してくださいf(10)。値による呼び出しでは、コンパイラはスタックにプッシュ10するだけで、関数はその場で値にアクセスするだけです。参照渡しでは、コンパイラはテンポラリを作成し、それを で初期化し10、それへのポインタを関数に渡す必要があります。関数内では、コンパイラは値にアクセスするたびに間接参照を生成する必要があります。

また、関数内の変更から保護しても、読みやすさにはあまり役立ちません。関数が参照パラメーターを取らない場合、関数の内部を見なくても、引数として渡す変数を変更できないことがわかります。将来、誰かが関数をどのように変更するかに関係なく。(関数がグローバル状態を変更することを許可されるべきではないと主張することさえできます。これは実装をrand()かなり困難にします。しかし、オプティマイザには確実に役立ちます。)

于 2012-07-31T08:51:02.450 に答える
3

C は引数を値渡しするためです。

Kernighan & Richtie 第 2 版より: (1.8 値による呼び出し) 「C では、すべての関数引数は「値」で渡されます」

于 2012-07-31T08:45:54.903 に答える
1

関数で実際のパラメーターを変更したい場合は、C++で参照して渡すことができます。

void foo(int& i)
{
    i++;
}
于 2012-07-31T08:49:42.860 に答える
0

関数が元の値を変更しないようにするためです。

于 2012-07-31T08:49:57.413 に答える
0

理由の1つは効率だと思います。ポインタを介して直接値にアクセスする方が、効率的です。もう1つの利点は、引数を値またはポインターで渡すことを選択できるC / C ++の方法であり、ポインターで渡すことしかできない方法です。ただし、最も重要なことは、値を渡すことは、関数がコードの残りの部分から分離されていることを意味し、関数内の変数への変更がコードの残りの部分に影響を与えないことを意味します。そうでなければ、はるかに多くのバグが発生し、コーディングがより困難になると私を信じてください。

于 2012-07-31T08:49:58.320 に答える
0

Cが値渡しである理由の1つは、参照渡しであるFORTRANで、「CALL MYROUTINE(3)」のような呼び出しでサブルーチンを呼び出すことができ、サブルーチンが値を変更する可能性があるためです。その議論の。次に、プログラムのその時点以降、「3」はサブルーチンによって与えられた値を持ちます。

当然、これが起こったとき、それは大きな混乱を引き起こしました。ソースコードが見た目とは非常に異なることをしたため、バグを見つけるのが難しくなりました。

したがって、人々がプログラミング言語について学んだように、言語の設計に使用される原則の1つは、コードを理解しにくくしたり、バグを起こしやすくしたりすることを避けることです。(この原則は、プログラミング言語により表現力を与え、コンパイラーがコードを最適化できるようにすることも求められているため、常にうまく適用されるとは限りません。)

于 2012-07-31T09:34:01.590 に答える
0

パラメータの値を変更するには、「&」記号を使用して参照渡しする必要があります。

これが行われる理由は、変数に変更を適用するかどうかを選択できるようにするためです。値で渡す場合、それが変更されず、プログラムでエラーが発生しないことを確認できます。

于 2012-07-31T08:51:46.570 に答える
-1

あなたがしようとしていることに応じて、これを行うことができます:

int n = 1;

n = foo(n);

変更後に foo(int i) が i を返すことを確認してください。

于 2012-07-31T08:51:56.837 に答える