私は、参照解除演算子、演算子のアドレス、および一般的なポインターについてかなりよく理解しています。
ただし、次のようなものを見ると混乱します。
_int* returnA() {
int *j = &a;
return j;
}
int* returnB() {
return &b;
}
int& returnC() {
return c;
}
int& returnC2() {
int *d = &c;
return *d;
}
_
returnA()
では、ポインターを返すように求めています。 j
がポインターであるため、これが機能することを明確にするだけですか?returnB()
では、ポインターを返すように求めています。ポインターがアドレスを指しているため、returnB()
が機能する理由は、_&b
_を返しているためです。returnC()
では、int
のアドレスが返されるように求めています。 c
を返すと、_&
_演算子はc
?に自動的に「追加」されますか?returnC2()
で、int
のアドレスが返されることを再度求めています。ポインターがアドレスを指すため、_*d
_は機能しますか?A、b、cがグローバルとして整数として初期化されると仮定します。
私の4つの質問すべてで正しいかどうかを誰かが検証できますか?
ReturnA()では、ポインターを返すように求めています。 jがポインタであるため、この動作を明確にするだけですか?
はい、int *j = &a
はj
を指すようにa
を初期化します。次に、j
の値、つまりa
のアドレスを返します。
ReturnB()では、ポインターを返すように求めています。ポインターがアドレスを指しているので、returnB()が機能する理由は、bを返しているからです。
はい。ここでは、上記と同じことが1つのステップで発生します。 &b
は、b
のアドレスを提供します。
ReturnC()で、返されるintのアドレスを要求しています。 cを返すと、演算子は自動的に追加されますか?
いいえ、返されるのはintへの参照です。参照は、ポインタと同じようにアドレスではありません-変数の単なる代替名です。したがって、変数の参照を取得するために&
演算子を適用する必要はありません。
ReturnC2()で、再びintのアドレスを返すように求めています。ポインターがアドレスを指すため、* dは機能しますか?
繰り返しますが、返されるのはintへの参照です。 *d
は、c
が指す元の変数c
(それが何であれ)を指します。そして、これはreturnC
のように暗黙的に参照に変換できます。
ポインタは一般にアドレスを指しません(ただし、_int**
はintへのポインタへのポインタです)。ポインタare何かのアドレス。 something*
のようなポインターを宣言すると、そのsomething
はポインターが指すものになります。したがって、上記の例では、int**
はint*
へのポインターを宣言しますが、これはたまたまポインターそのものです。
ピーターはあなたの質問に答えましたが、明らかに紛らわしいのはシンボル*
と&
です。これらを回避するのが難しい部分は、両方とも間接に関係する2つの異なる意味を持つことです(乗算の*
とビット単位の&
の3番目の意味を除く)。
*
は、typeの一部として使用すると、型がポインターであることを示します。int
は型であるため、int*
は、 int型、およびint**
はポインターからポインターへのポインター型です。
&
がtypeの一部として使用されている場合、タイプが参照であることを示します。 int
は型なので、int&
はintへの参照です(参照への参照などはありません)。参照とポインターは類似のものに使用されますが、それらはまったく異なり、互換性はありません。参照は、既存の変数の別名または別名として最もよく考えられます。 x
がint
である場合、int& y = x
を割り当てるだけで、y
の新しい名前x
を作成できます。あとがき、x
とy
は同じ整数を参照するために交換可能に使用できます。これの2つの主な意味は、参照をNULLにすることはできません(参照する元の変数がある必要があるため)、および元の値を取得するために特別な演算子を使用する必要がないことです(単なる代替名であるため、ポインタではありません)。参照を再割り当てすることもできません。
*
を単項演算子として使用すると、dereference(参照types!とは関係ありません)と呼ばれる操作を実行します。この操作は、ポインターに対してのみ意味があります。ポインターを逆参照すると、ポインターが指しているものが返されます。したがって、p
がintへのポインタである場合、*p
はポイントされているint
です。
&
を単項演算子として使用すると、address-ofと呼ばれる操作が実行されます。それはかなり自明です。 x
が変数の場合、&x
はx
のアドレスです。変数のアドレスは、その変数の型へのポインターに割り当てることができます。したがって、x
がint
である場合、&x
はint*
型のポインターに割り当てることができ、そのポインターはx
を指します。 。例えば。 int* p = &x
を割り当てると、*p
を使用してx
の値を取得できます。
型の接尾辞&
は参照用であり、単項演算子&
とは関係ありません。これは、ポインターで使用するアドレスを取得することに関係しています。 2つの用途は完全に無関係です。また、型接尾辞としての*
はポインターを宣言し、単項演算子としての*
はポインターに対してアクションを実行します。
タイラー、それは非常に有用な説明でしたが、この違いをさらに明確にするために、Visual Studioデバッガーを使用していくつかの実験を行いました。
int sample = 90;
int& alias = sample;
int* pointerToSample = &sample;
Name Address Type
&alias 0x0112fc1c {90} int *
&sample 0x0112fc1c {90} int *
pointerToSample 0x0112fc1c {90} int *
*pointerToSample 90 int
alias 90 int &
&pointerToSample 0x0112fc04 {0x0112fc1c {90}} int * *
PointerToSample Sample/alias
_______________......____________________
0x0112fc1c | | 90 |
___________|___.....__|________|_______...
[0x0112fc04] ... [0x0112fc1c
ReturnC()およびreturnC2()では、アドレスを返すことを求めていません。
これらの関数は両方とも、オブジェクトへの参照を返します。
参照は何かのアドレスではなく、何かの代替名です(これは、コンパイラがアドレスを使用してオブジェクトを表すことができる(または状況に応じない)ことを意味する場合があります(または、レジスタにそれ))。
参照が特定のオブジェクトを指していることを知っているすべて。
参照自体はオブジェクトではなく、単なる代替名です。
すべての例は、未定義のランタイム動作を生成します。実行が関数を離れると消えるアイテムへのポインターまたは参照を返しています。
明確にしましょう:
int * returnA()
{
static int a; // The static keyword keeps the variable from disappearing.
int * j = 0; // Declare a pointer to an int and initialize to location 0.
j = &a; // j now points to a.
return j; // return the location of the static variable (evil).
}
関数では、変数j
がa
の一時的な場所を指すように割り当てられます。関数を終了すると、変数a
は消えますが、以前の場所はj
を介して返されます。 a
が指す場所にはj
が存在しないため、*j
にアクセスすると未定義の動作が発生します。
関数内の変数は、他のコードによる参照またはポインターを介して変更しないでください。未定義の動作を生成しますが、発生する可能性があります。
物足りないので、返されるポインターは定数データを指すものとして宣言する必要があります。返される参照はconstである必要があります。
const char * Hello()
{
static const char text[] = "Hello";
return text;
}
上記の関数は、定数データへのポインターを返します。他のコードは静的データにアクセス(読み取り)できますが、変更できません。
const unsigned int& Counter()
{
static unsigned int value = 0;
value = value + 1;
return value;
}
上記の関数では、value
は最初のエントリでゼロに初期化されます。この関数を次に実行すると、value
が1ずつ増加します。この関数は、定数値への参照を返します。これは、他の関数が値を(遠くから)変数のように使用できることを意味します(ポインタを間接参照する必要はありません)。
私の考えでは、ポインターはオプションのパラメーターまたはオブジェクトに使用されます。オブジェクトが存在する必要があるときに参照が渡されます。関数内では、参照されたパラメーターは値が存在することを意味しますが、ポインターを参照解除する前にNULLをチェックする必要があります。また、参照を使用すると、ターゲットオブジェクトが有効であることがより確実になります。ポインターは無効なアドレス(null以外)を指し、未定義の動作を引き起こす可能性があります。
意味的には、参照はアドレスとして機能します。ただし、構文的には、それらはコンパイラの仕事であり、あなたの仕事ではありません。参照を他の参照にバインドし、元のオブジェクトを参照させるなど、参照を元のオブジェクトであるかのように扱うことができますこの場合、ポインタ演算に別れを告げます。
その欠点は、参照するものを変更できないことです-構築時にバインドされます。