web-dev-qa-db-ja.com

間接参照演算子(*)がポインターの宣言にも使用されるのはなぜですか?

これが適切なプログラミングの質問であるかどうかはわかりませんが、それはいつも私を悩ませてきたものであり、私だけではないかと思います。

最初にC++を学んだとき、私は参照の概念を理解していましたが、ポインターは私を混乱させました。なぜ聞くの?ポインタを宣言する方法のため。

次のことを考慮してください。

_void foo(int* bar)
{
}


int main()
{
    int x = 5;
    int* y = NULL;

    y = &x;
    *y = 15;     
    foo(y);

}
_

関数foo(int*)は、パラメーターとしてintポインターを取ります。 yintポインターとして宣言したので、yfooに渡すことができますが、最初にC++を学習したときに、_*_を関連付けました。逆参照のあるシンボルなので、逆参照されたintを渡す必要があると思いました。 _*y_をfooに渡そうとしますが、これは明らかに機能しません。

ポインターを宣言するための別個の演算子を使用する方が簡単ではなかったでしょうか。 (または逆参照用)。例えば:

_void test(int@ x)
{
}
_
54
diggingforfire

C言語の開発 で、デニス・リッチーは彼の推論を次のように説明しています。

Cをその前身と最も明確に区別する2番目の革新は、このより完全な型構造、特に宣言の構文でのその表現です...任意の型のオブジェクトが与えられると、いくつかを配列に集める新しいオブジェクトを記述できるはずです。 、関数からそれを生成するか、それへのポインタです... [これ]名前が通常現れる式構文の宣言構文を反映した名前の宣言構文に導かれます。したがって、

_int i, *pi, **ppi;_は、整数、整数へのポインター、整数へのポインターへのポインターを宣言します。これらの宣言の構文は、式で使用すると_i, *pi, and **ppi_がすべてint型を生成するという観察結果を反映しています。

同様に、int f(), *f(), (*f)();は、整数を返す関数、整数へのポインターを返す関数、整数を返す関数へのポインターを宣言します。 int *api[10], (*pai)[10];は、整数へのポインターの配列、および整数の配列へのポインターを宣言します。

これらすべての場合において、変数の宣言は、宣言の先頭で指定されたタイプの式での使用法に似ています

構文の偶然が、言語の複雑さの認識に寄与しました。 Cで*と綴られる間接演算子は、BCPLやBと同様に、構文的に単項接頭演算子です。これは単純な式ではうまく機能しますが、より複雑なケースでは、解析を指示するために括弧が必要です。たとえば、関数によって返される値を介した間接参照と、ポインターで指定された関数の呼び出しを区別するには、それぞれ*fp() and (*pf)()と記述します。式で使用されるスタイルは宣言に引き継がれるため、名前が宣言される可能性があります

int *fp(); int (*pf)();

より華やかでありながら現実的なケースでは、事態はさらに悪化します。int *(*pfp)();は、整数へのポインターを返す関数へのポインターです。

2つの影響が発生しています。最も重要なのは、Cにはタイプを記述するための比較的豊富な方法のセットがあることです(たとえば、Pascalと比較して)。 Cのように表現力豊かな言語での宣言(たとえば、ALGOL 68)は、オブジェクト自体が複雑であるという理由だけで、同じように理解しにくいオブジェクトを記述します。 2番目の効果は、構文の詳細によるものです。 Cの宣言は、多くの人が理解するのが難しい「裏返し」のスタイルで読む必要があります。 Sethi [Sethi 81]は、間接演算子を接頭辞ではなく後置演算子として使用すると、ネストされた宣言と式の多くが単純になることを観察しましたが、それまでに変更するには遅すぎました。

83
Crashworks

このように書くと、理由はより明確になります。

int x, *y;

つまり、xと* yはどちらもintです。したがって、yはint *です。

14
Stuart Golodetz

これは、C++がCから継承したため、C++よりも前の言語の決定です。動機は宣言と使用が同等である、つまり、宣言int *p;が与えられた場合、式*pはタイプintint i;の場合と同じように、式iのタイプはintです。

委員会、および標準化の数十年前にC++を開発した委員会は、*は元の3つの意味を保持する必要がある

  • ポインタ型
  • 間接参照演算子
  • 乗算

*(および同様に&)の複数の意味が混乱していることを示唆するのは正しいことです。私はここ数年、言語の初心者にとって理解の大きな障壁であると考えてきました。


C++用に別の記号を選択してみませんか?

後方互換性が根本的な原因です...以前は演算子ではなかったものを新しい意味に変換してCプログラムを壊すよりも、新しいコンテキストで既存のシンボルを再利用するのが最善です。


Cに別の記号を選んでみませんか?

確実に知ることは不可能ですが、なされる可能性のある、そしてなされてきたいくつかの議論があります。何よりもまず、次のような考えがあります。

[an]識別子が宣言子と同じ形式の式に現れると、指定されたタイプのオブジェクトが生成されます。 {K&R、p216}

これは、Cプログラマーが傾向がある理由でもあります[要出典] アスタリスクを左ではなく右に揃えることを好みます。つまり、次のようになります。

int *ptr1; // roughly C-style
int* ptr2; // roughly C++-style

両方の種類が両方の言語のプログラムにありますが、さまざまです。

エキスパートCプログラミング:ディープCシークレット には次のものが含まれます:そして、オブジェクトの宣言はその使用のように見えるべきであるというC哲学があります。

Cプログラミング言語、第2版(別名K&R) には次のものが含まれます:宣言子は、その識別子が同じ式に現れるときにアサーションとして読み取られます。宣言子としてフォームを使用すると、指定されたタイプのオブジェクトが生成されます。

私はファンデルリンデンがそれを置く方法を好みます。

5
sarnold

ハハ、私はあなたの痛みを感じます、私はまったく同じ問題を抱えていました。

ポインタが何かのアドレスであることは理にかなっているので、ポインタは&intとして宣言する必要があると思いました。

しばらくして、自分で考えた後、Cのすべてのタイプを読み取る必要があります後方のように

int * const a

私のためです

a constant something, when dereferenced equals an int逆参照でなければならないものは、ポインタでなければなりません。

3
Binarian