Cでは、*は演算子ですか、それとも宣言の型の一部ですか?


9

Cでは*、間接演算子または間接参照演算子と呼ばれます。ステートメントで使用した場合の動作を理解しています。*por * pは、単項演算子であることを考えると、意味があります。

ただし、宣言で*は、a が使用されることがあります。

void move(int *units) { ... }

または

int *p = &x;

ここで演算子を使用しているのは不思議です。次のようなことはできません

void move(int units++) { ... }

どこ++にも単項演算子です。これ*は間接演算子でも*ありますか、それともポインタの型について何かを言っている別の意味がありますか?(これが当てはまる場合、私はたとえばint* units宣言などで使用することを検討していますが、int x = *p;それ以外は明確にするためです。)

、この答えはと言われています

C標準では、*演算子に対して2つの意味のみを定義しています。

  • 間接演算子
  • 乗算演算子

これ*は実際にはタイプの一部であると主張する人々も見ました。これは私を混乱させます。


1
ではvoid move(int *units) { ... }、これは間接演算子です。 タイプの一部と考えられるので、書くこともできますがvoid move(int* units) { ... }、私は前者のスタイルを好みます。両方とも「intポインター」として読みます。stackoverflow.com/a/8911253
Robert Harvey

回答:


16

C言語の最小片は字句キーワードなど、(例えばintifbreak)、識別子(例えばmoveunits)など。

*句読点と呼ばれる字句要素です(たとえば{}()+)。区切り記号は、それがどのように使用されるかによって、異なる意味を持つ可能性があります。

句読点は、独立した構文上および意味上の意味を持つ記号です。コンテキストによっては、実行する操作を指定する場合があります(...)。この場合、オペレーターとして知られています。

式に関するC11標準の第6.5章で*は、説明したとおり、単項演算子(セクション6.5.3.2、間接参照)および乗法演算子(セクション6.5.5)として定義されています。ただし*、有効な式を作成する文法規則に従って、演算子としてのみ解釈されます。

しかし、概念に関する第6.2章もあり、型へのポインタは派生型であることを説明しています。ポインター宣言子に関するセクション6.7.6.1はより正確です:

宣言 '' T D1 ''で、D1の形式が
* type-qualifier-list-opt Dで
あり、宣言 '' T D ''のidentに指定された型が '' derived-declarator-type-listである場合T ''の場合、identに指定されたタイプは、 '' Tへの派生宣言子タイプリストリスト修飾子リストポインター ''です。

これは確かに*派生型の一部になります(*つまり、「ポインタ」があることを意味しますが、何かを指すものの種類を示すには、常に別の型が必要です)。

備考:これには矛盾はありません。あなたは英語を話すとき、日常的にそのような区別された字句言語要素の使い方をします。「ハンドル」は何かを指定し、「処理する」とは何かをすることを意味します。


つまり、この解釈は、(他の回答から)それを見る一般的な方法です:int * aこのステートメントは、次のように解釈される可能性があります。-実際には*が宣言で独立した意味を持っている場合
トーム

@トム正確!
クリストフ

7

Cでは、宣言の間接参照は、型の一部よりも変数の一部として読み取る方が適切です。私が持っている場合:

int *a;

このステートメントは、次のように解釈される可能性があります。a逆参照されたときにtype であるという名前の変数を宣言しますint

これは、複数の変数を一度に宣言する場合に重要です。

int *a, b;   (1)
int* a, b;   (2)

両方の宣言は等価であり、両方でab同じタイプではない- aintへのポインタであり、bint型です。しかし、宣言(2)は、「ポインタ型」が実際にの宣言である場合、型の一部であるかのように見えます*a


これは、1と2の間の混乱であることが多いため、これを提示するのに非常に良い点です。ただし、aの型は「intへのポインタ」であるため、*がaにのみ適用される場合でも、それは間違いなく型の一部です。たとえば、typedefで使用できます(初期化子には使用できません)。
クリストフ

2つの宣言を決定する際に良い議論を探していたので、これはそれを裏付けた。変数をリストとして宣言することはめったにありませんが、それでも、これからは、最初の形式をすべてのケースで最も明確なものとして採用します。よろしくお願いします!
ニュートピア

4

他の正しい答えに追加するには:

これは、潜在的な使用法の表現を反映する単なる宣言式です。IIRC、カーニガンまたはリッチーがそれを実験と呼んだと思います!以下は、わずかにサポートしているテキストです。

70年代に戻って、カーニハンとリッチーがCプログラミング言語を書いているとき、彼らは、訓練されていない目でどのように宣言がすぐに読めなくなるかについて非常に正直でした。

Cは、その宣言の構文、特に関数へのポインターが関係するものについて、時々厳しく批判されます。構文は、宣言使用法を一致させる試み です。単純なケースではうまく機能しますが、宣言を左から右に読み取ることができず、括弧が過剰に使用されるため、難しいケースでは混乱する可能性があります。[...]

(私の強調)

http://codinghighway.com/2013/12/29/the-absolute-definitive-guide-to-decipher-c-declarations/

そのため、はい、そうです。これらは実際には宣言式に適用される演算子と同じですが、一部の演算子のみが意味を持っていることがわかります(例:++はそうではありません)。

そのような星は確かに宣言されている個々の変数の型の一部です。ただし、他の投稿者が指摘しているように、同時に宣言されている他の変数(つまり、同じ宣言ステートメント)に引き継がれません(より正確には、それはできません)。各変数は、 (最終的な)ターゲット、そして独自の(適用するかどうかにかかわらず)ポインター、配列、および関数演算子を取得して、その型を完成させます。


経験豊富なプログラマは(1)好む傾向にある理由はここにあるint *p;int* p;及び(2)の言語がより可能にもかかわらず、宣言ごとに1つだけの変数を宣言します。

これをint (*p);法的宣言に追加すると、これらの括弧は単にグループ化され、この単純なケースではと何も変わりませんint *p。これは、(非宣言的な)式(i) >= (0)がと同じi >= 0であると言うことと同じです()

ただし、プログラマが他の形式よりも1つの形式を好む理由の別の例として、int (*p);vsを検討してくださいint(* p);。おそらく、括弧やスターなどが基本型ではなく変数にどのように適用されるかを確認できます(つまり、許可されるすべての括弧を追加することによって)。


1

この関数宣言では:

void move(int *units);

これ*はタイプの一部、つまりですint*。オペレーターではありません。これが、多くの人が次のように書く理由です。

void move(int* units);

1

のみ*[]および()演算子は(C ++が追加されます宣言でどんな意味を持っている&が、我々はここでそのに行くことはありません)。

宣言で

int *p;       

int-ness p型指定で指定されたintのポインタらしをしながら、pで指定される宣言子 *p

タイプのはp「へのポインタですint」。この型は、型指定子intと宣言子の組み合わせによって完全に指定されます*p

宣言では、宣言子は、宣言されているものの名​​前(p)と、型指定子(「ポインタ」)によって提供されない追加の型情報を紹介します。

T v;     // v is a single object of type T, for any type T
T *p;    // p is a pointer to T, for any type T
T a[N];  // a is an N-element array of T, for any type T
T f();   // f is a function returning T, for any type T

これは重要です-ポインター性、配列性、および関数性は、型指定子1ではなく、宣言子の一部として指定されます。あなたが書くなら

int* a, b, c;

次のように解析されます

int (*a), b, c;

したがってa、へのポインタとしてのみ宣言されますintbそしてc、通常のように宣言されているintの。

*[]、および()演算子は任意の複雑なタイプを作成するために組み合わせることができます。

T *a[N];      // a is an N-element array of pointers to T
T (*a)[N];    // a is a pointer to an N-element array of T
T *(*f[N])(); // f is an N-element array of pointers to functions 
              // returning pointer to T

T *(*(*(*f)[N])())[M]  // f is a pointer to an N-element array of pointers
                       // to functions returning pointers to M-element
                       // arrays of pointers to T

お知らせその*[]()彼らは式の中で行うことを宣言して、同じ優先順位規則に従います。 宣言と式の両方と*a[N]同様*(a[N])に解析されます。

これで実現する本当に重要なことは、宣言の形式がコード内の式の形式と一致することです。元の例に戻ると、という名前の整数へのポインターがありますp。その整数値を取得したい場合は、次のように*演算子を使用して逆参照しpます。

x = *p;

種類発現が *pあるint宣言から追従します、

int *p;

同様に、ポインタの配列がありdouble、特定の値を取得したい場合は、配列にインデックスを付けて結果を逆参照します。

y = *ap[i];

再度、タイプ発現が *ap[i]あるdouble宣言から追従します、

double *ap[N];

なぜしない++宣言のような役割を果たし*[]または()?または+or ->または&&?の ような他の演算子

まあ、基本的に、言語の定義がそう言っているからです。それだけではさておき設定し*[]、と()あなたは、ポインタ、配列、および関数型を指定できるようにする必要があるため、宣言内の任意の役割を果たしています。個別の「increment-this」タイプはないため++、宣言の一部である必要はありません。何の「ビットごと-この」タイプは単項の必要がないので、ありません&|^、または~宣言のいずれかの一部であることを。使用タイプの場合は.メンバー選択演算子を、我々は使用structしてunion宣言でタグを。->演算子を使用する型の場合、宣言子で演算子と組み合わせてタグstructunionタグを使用します*


  1. もちろん、次のように、ポインタ、配列、および関数型のtypedef名を作成できます。
    typedef int *iptr;
    iptr a,b,c; // all three of a, b, and c are pointers to int
    繰り返しになりますが、typedef名のポインター性を指定するのは宣言 *iptr子です。


1

C標準では、*演算子に対する2つの意味のみを定義しています。

  • 間接演算子
  • 乗算演算子

* 演算子には2つの意味があります。これらが* トークンの唯一の意味であるとは限りません。

のような宣言で

int *p;

これ*は演算子ではありません。それは宣言の構文の一部です。

Cの宣言構文は、上記の宣言は「と読むことができるので、その表現の構文を反映することを意図している*pタイプのものであるint」、そこから我々はそれが推測できるpタイプのものですint*。ただし、これは確固とした規則ではなく、規格のどこにも明記されていません。代わりに、宣言の構文は、式の構文とは別に独自に定義されます。そのため、たとえば、式で使用できるほとんどの演算子は、宣言でも同じ意味で使用できません(ただし、の+演算子のように、宣言の一部である式の一部である場合は除きますint array[2+2];)。

原理はしない「宣言ミラーが使用」の場合はかなりの仕事は次のとおりです。

int arr[10];

存在する場合arr[10]、タイプはどこになります。int

そして実際には、*トークンのさらに別の使用法があります。[*]可変長配列を示すために配列パラメーターを宣言できます。(これはC99で追加されました。)これ*は乗算でも逆参照でもありません。シェルのワイルドカード構文に触発されたのではないかと思います。


「宣言は使用に従う」という概念がうまく機能しない大きな場所は、ポインタの初期化です。宣言の効果はint *p = 0;用法* p = 0; `に似ていますが、意味が大きく異なります。
スーパーキャット2018

@supercat:もちろん。初期化は常にオブジェクトのタイプ(この場合はint*)と一致します。
キーストンプソン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.