10

私は何年もC++でプログラミングしていないので、ポインターで思い出をリフレッシュすることにしました。

2つの数値を交換する典型的な例では、例は次のとおりです。

void swapPointersClassic(double *num1, double *num2) 
{
  double temp;
  temp = *num1;
  *num1 = *num2;
  *num2 = temp;
}

これにより、関数を次のようswapPointersClassic(&foo, &bar);に呼び出すことができます。変数fooとbarの両方のメモリアドレスを渡すため、関数は値を取得してスワップを実行します。しかし、私は疑問に思い始めました、なぜ私は次のことができないのですか?

void swapPointers(double *num1, double *num2)
{
  double *temp;
  temp = num1;
  num1 = num2;
  num2 = temp;
}

(double値* num1を格納するための完全な一時ストレージの代わりに)メモリアドレスnum1を格納するために十分な一時ストレージを作成するだけでよいので、それは私にはもっと理にかなっているようです。ただし、関数スコープはポインタスワッピングの効果を制限しているようです。呼び出しを行った後swapPointers(&foo, &bar);、関数swapPointers内​​でfooとbarが実際にスワップされていることがわかります。swapPointers関数を終了すると、fooとbarはスワップされなくなります。誰かが私がこれが事実である理由を理解するのを手伝ってもらえますか?この動作は、典型的な値渡しアプローチを思い出させますが、ここではポインターを渡します。つまり、これらのポインターが指す値にのみ触れることができ、ポインター自体には触れることができないということですか?

4

8 に答える 8

8

実際には、実際にはポインタ渡しではありません。2 つのポインターを値で渡します。ポインターを渡すだけでは不十分です。ポインターを逆参照する必要があります。ポインター (のコピー) を逆参照する行為によって魔法が起こり、それがスコープ トラバーサルが達成される実際の場所になります。

関数の引数の変更は、引数自体がポインターであっても、関数に対してローカルです。それらが指すメモリにアクセスするには、それらを逆参照する必要があります(繰り返しますが、「ポインターによる受け渡し」はポインターの受け渡し以上のものです-ポインターを渡し、それらを適切に使用しています)。

また、次のことも考慮してください。2 番目のアプローチが機能し、2 つのポインターが交換された場合、変数のアドレスが交換されたことを意味します。これはあまり意味がありません。

ちなみに、C++ には真の参照渡し呼び出し規約があるため 、ポインターをいじる必要はありません。

void swap(double &a, double &b)
{
    double temp = a;
    a = b;
    b = temp;
}

float a = 1, b = 2;
swap(a, b);

(コンパイラが実際にポインタを使用してこの動作を実現する可能性が最も高いのは実装の問題ですが、少なくとも頭痛の種はありません。)

于 2012-11-08T17:58:25.297 に答える
6

値ではなくポインターを交換する場合は、ポインターのポインターを渡す必要があります。

void swapPointers(double** num1, double** num2)
{
  double* temp = *num1;
  *num1 = *num2;
  *num2 = temp;
}

http://ideone.com/iklCtaで例を見ることができます。

参照でも作業できます。

void swapPointers(double*& num1, double*& num2) {
  double* temp = num1;
  num1 = num2;
  num2 = temp;
}

例: http://ideone.com/w4k9mV

これは、巨大なオブジェクト (例: 画像データ) を操作していて、多くのメモリを移動したくないが、参照だけを移動したい場合に便利です。

于 2012-11-08T17:55:36.377 に答える
2

これは良い質問ですが、理解できたら、自分で書く代わりに、std::swapまたは使用してください。std::iter_swap

fooとがポインタの場合bar、呼び出しは2つのポインタ間でアドレスstd::swap(foo, bar)を交換します。ポインタを呼び出すか、間接参照して、ポインタが指すオブジェクトを交換します。std::swap(*foo, *bar)std::iter_swap(foo, bar)

于 2012-11-08T18:30:44.033 に答える
0

ポインター自体を値で渡しています。num1およびnum2は、呼び出し元が関数を呼び出すポインターのローカル コピーです。そのため、変更は関数に対してローカルな変数に対してのみ行われたため、関数が終了したときに呼び出しサイトでスワッピングが表示されません。

代わりに、ポインターへの参照を取るように関数を変更すると、スワッピング コードが意図したとおりに機能します。

void swapPointers(double*& num1, double*& num2) { ... }

ここで、関数パラメーターは、呼び出し元が関数に渡すポインターのエイリアスであり、それらに対して行うことはすべて、呼び出し元のコンテキストのポインターにも影響します。

于 2012-11-08T17:56:23.333 に答える
0

電話すると

swapPointersClassic(&foo, &bar)

stack上の 2 つのアイテムのアドレスを取得しています。これらの値は一時的に渡され、値によってコピーされます。関数本体では、これらの一時変数が指している場所を交換しますが、foo と bar が指している場所には移動しません。

最も重要なことは、 foo と bar がstackに存在することです。C++ では、スタック内の変数の場所を変更することはできません。値はスタック上のスポットにマップされ、そこに存在します。その値が存在する場所へのポインターを取得して、ポインター渡し (参照渡しと同様) を実行できますが、その一時ポインターが指す場所を変更するだけでは、ポイント先のオブジェクトが存在する場所は変更されません。

于 2012-11-08T17:56:53.267 に答える
0

関数に渡すときdouble *num1は、ポインターを値で渡します。これにより、double のアドレスを保持する関数スコープに変数が作成されます。それに割り当てると、関数スコープ内のポインターの値が変更されます。

ポインターを double に渡し、逆参照し、代入して double をスワップする必要があるのと同じように、ポインターをポインターに渡し、逆参照し、代入してポインターをスワップする必要があります。

于 2012-11-08T17:57:53.590 に答える
0

ポインター 101: ポインターは、家の住所が書かれた紙です。

&bar と書くことで、あなたは bar という家を取り、その住所を無記名のスクラップ紙に書き留めていることになります。

次に、そのポインターを引数として関数を呼び出します。ここで起こることは、関数が一枚の紙の別のコピーを作成することです。

その後、swap 関数はそのようなローカル コピーを 2 つ取得し、それらに書き込まれているアドレスを交換します。

したがって、関数の外では何もしません。クリア?

基本的に、住所の関数内にコピーがなくても、作業しているのは家の住所が記載されたスクラップ紙だけです。このような紙に変更を加えても、house bar または foo に格納されているデータを実際に移動することはできません。

于 2012-11-08T18:04:52.320 に答える
0

この質問に対する適切な回答が既に投稿されているため、いくつかのことを明確にします。

参照を使用して 2 つの変数の値を交換することは、一般的に悪い習慣であり、避ける必要があります。その理由は、参照が再割り当てされることは想定されていないためです。次のコードを見てください。

1. void swap(double &a, double &b)
2. {
3.    double temp = a;
4.    a = b;  //Not what you think
5.    b = temp;
6. }
7.
8. float a = 1, b = 2;
9. swap(a, b);

4行目と5行目で、「参照を再割り当てしない」という一般的なルールに明らかに違反しています。

変数の値を本当に変更する必要がある場合は、常に「参照渡し」よりも「ポインター渡し」を優先してください。次のコードは理にかなっていて、IMO の良い習慣です。

1.  void swapPointersClassic(double *num1, double *num2) 
2.  {
3.   double temp;
4.   temp = *num1;
5.   *num1 = *num2;
6.   *num2 = temp;
7.  }    
8.
9.  float a = 1, b = 2;
10. swap(&a, &b);

最初の例で見たように、参照はよりクリーンで使いやすく、情報をより適切に隠すことに同意しました。ただし、参照を再割り当てすることはできません。最初に 1 つのオブジェクトを指し、次に別のオブジェクトを指す必要がある場合は、ポインターを使用する必要があります。

経験則:

  • 参照が機能する場合は、ポインターを使用しないでください。
  • 別の変数への参照を再割り当てしようとしないでください。できません。
于 2015-07-15T15:10:39.633 に答える