ポインター宣言でのアスタリスクの配置


92

私は最近、C / C ++を最終的に習得する必要があると最近決定しました。ポインタについて、またはより正確には、その定義について、私が本当に理解していないことが1つあります。

これらの例はどうですか:

  1. int* test;
  2. int *test;
  3. int * test;
  4. int* test,test2;
  5. int *test,test2;
  6. int * test,test2;

さて、私の理解では、最初の3つのケースはすべて同じことをしています:Testはintではなく、1へのポインターです。

2番目の例のセットは、もう少しトリッキーです。ケース4では、testとtest2の両方がintへのポインターですが、ケース5ではtestのみがポインターですが、test2は「実際の」intです。ケース6はどうですか?ケース5と同じ?


10
C / C ++では、空白は意味を変えません。
スルタン

19
7. int*test;
ジンクォン・

3
+1について質問したいと思ったので+1。この質問を読んで、私は4-6について私が考えたこともないことを学びました。
広大なスーパーマン2016

@Sulthan 99%の確率でそうですが、常にそうとは限りません。私の頭の中で、テンプレート型のスペース要件(C ++ 11以前)にテンプレート型のタイプがありました。書かなければならなかったように右シフトとして扱われていません。Foo<Bar<char>>>>> >
AnorZaken

3
@AnorZakenその通りです、それはかなり古いコメントです。スペースが意味を変える複数の状況があります。たとえば、インクリメント++演算子はスペースで分割できない、識別子はスペースで分割できない(そして、結果はコンパイラーに合法である可能性がありますが、ランタイム動作が未定義です)。正確な状況を定義することは、C / C ++の構文の混乱を考慮すると非常に困難です。
スルタン

回答:


129

4、5、および6は同じものであり、テストのみがポインタです。2つのポインターが必要な場合は、以下を使用する必要があります。

int *test, *test2;

または、さらに良い(すべてを明確にするため):

int* test;
int* test2;

3
では、ケース4は実際には死の罠なのでしょうか?int * test、test2が最初の変数のみをポインターにする理由を説明する仕様または参考資料はありますか?
Michael Stum

8
@ Michael Stum C ++なので、論理的な説明があると本当に思いますか。
ジョーフィリップス

6
K&R(Cプログラミング言語)をお読みください。これは、これらすべてを非常に明確に説明しています。
Ferruccio

8
ケース4、5、6は「死の罠」です。これが、多くのC / C ++スタイルのガイドがステートメントごとに1つの宣言しか提案しない理由の1つです。
マイケル・バー、

14
空白は、Cコンパイラにとって重要ではありません(プリプロセッサは無視されます)。したがって、アスタリスクとその周囲の間にいくつのスペースがあってもなくても、まったく同じ意味になります。
ephemient

45

アスタリスクの周りの空白は意味がありません。3つとも同じ意味です。

int* test;
int *test;
int * test;

int *var1, var2」は単に人を混乱させることを意図した邪悪な構文であり、避けるべきです。次のように拡張されます。

int *var1;
int var2;

16
忘れないでくださいint*test
Mateen Ulhaq、

1
アスタリスクの前後のスペースは、単に美学の問題です。ただし、Googleコーディング標準はint *testgoogle-styleguide.googlecode.com/svn/trunk/…)に準拠しています。ただ一貫している

@SebastianRaschka Google C ++スタイルガイドでは、どちらのアスタリスクの配置も明示的に許可されています。読んでから変わったのかもしれません。
Jared Beck 2014年

33

多くのコーディングガイドラインでは、1行に1つの変数のみを宣言することを推奨しています。これにより、この質問をする前にあった種類の混乱を回避できます。私が一緒に働いたほとんどのC ++プログラマーはこれに固執しているようです。


私が知っている少しの余談ですが、私が便利だと思ったのは、宣言を逆に読むことです。

int* test;   // test is a pointer to an int

これは、特にconstポインターの宣言を開始し、それがconstであるのがポインターであるのか、それともポインターが指しているものがconstであるのかを知るのが難しい場合に、非常にうまく機能し始めます。

int* const test; // test is a const pointer to an int

int const * test; // test is a pointer to a const int ... but many people write this as  
const int * test; // test is a pointer to an int that's const

「1行に1つの変数」が役立つように見えますが、アスタリスクが左側または右側にある状況は完全には解決していません。野生のコードの1つのバリアントが優勢であると確信しています。一部の国では右側を運転しているように、他の国では間違った方向を運転しています(英国など)。;-)
シェビー

残念ながら、野生への冒険から、両方のスタイルがたくさんあります。私のチームでは、同意したスタイルのclang形式を使用しています。これは少なくとも、私たちのチームが作成するすべてのコードが、空白の行き先が同じスタイルであることを意味しています。
スコットランガム

33

Clockwise Spiral Ruleを使用して、C / C ++宣言を解析します。

次の3つの簡単な手順があります。

  1. 未知の要素から始めて、らせん/時計回りの方向に移動します。次の要素に遭遇した場合、それらを対応する英語のステートメントで置き換えます。

    [X]または[]:配列Xのサイズ...または配列未定義のサイズ...

    (type1, type2):type1とtype2を渡す関数が戻ります...

    *:へのポインタ...

  2. すべてのトークンがカバーされるまで、これをらせん/時計回りの方向に続けます。
  3. 常に最初に括弧で何かを解決してください!

また、宣言は可能な限り個別のステートメントで行う必要があります(これはほとんどの場合に当てはまります)。


4
大変申し訳ありませんが、非常に恐ろしいです。
Joe Phillips、

7
それはありますが、より複雑な構造のいくつかについては非常に良い説明のようです
Michael Stum

@ d03boy:質問はありません-C / C ++宣言は悪夢になる可能性があります。
マイケル・バー、

2
「スパイラル」は意味がなく、「時計回り」ではありません。構文では右下左左上ではなく、右左のみに見えるため、「右左ルール」と名付けたいと思います。
ルスラン

私はこれを「右左左」ルールとして学びました。C ++の人々は、すべての型情報が左側にあるふりをすることを好むことが多く、これがint* x;従来のint *x;スタイルではなくスタイルにつながります。もちろん、間隔はコンパイラにとって重要ではありませんが、人間に影響を与えます。実際の構文の拒否は、読者を困らせ、混乱させる可能性のあるスタイル規則につながります。
エイドリアンマッカーシー、

12

他の人が述べたように、4、5、6は同じです。多くの場合、人々はこれらの例を使用*して、型ではなく変数に属しているという引数を作成します。これはスタイルの問題ですが、このように考えて記述する必要があるかどうかについては、いくつかの議論があります。

int* x; // "x is a pointer to int"

またはこのように:

int *x; // "*x is an int"

FWIW私は最初の陣営にいますが、他の人が2番目の形式について議論する理由は、それが(主に)この特定の問題を解決するためです。

int* x,y; // "x is a pointer to int, y is an int"

誤解を招く可能性があります。代わりに、あなたはどちらかを書くでしょう

int *x,y; // it's a little clearer what is going on here

または、2つのポインタが本当に必要な場合は、

int *x, *y; // two pointers

個人的には、1行あたり1つの変数に保持することをお勧めしますが、どちらのスタイルを使用するかは関係ありません。


6
これは偽物int *MyFunc(void)です。何といいますか?a *MyFuncint?を返す関数です。番号。int* MyFunc(void)言うまでもなく、を書く必要MyFuncがありますint*。したがって、私にはこれは明らかです。CおよびC ++の文法解析ルールは、変数宣言では単に間違っています。コンマシーケンス全体の共有型の一部として、ポインター修飾を含める必要があります。
v.oddou 2017年

1
しかし、*MyFunc() ですint。C構文の問題は、接頭辞接尾辞の構文が混在していることです。接尾のみを使用した場合でも、混乱はありません。
Antti Haapala 2018

1
最初のキャンプのような構造混乱につながる、言語の構文を戦うint const* x;私のように誤解を招くよう見つけます、a * x+b * y
エイドリアンマッカーシー、


5

4、5、および6では、testは常にポインターであり、ポインターでtest2はありません。C ++では、空白は(ほとんど)重要ではありません。


3

私の意見では、状況に応じて答えは両方です。一般的に、IMOでは、タイプではなくポインタ名の横にアスタリスクを置く方が適切です。例を比較:

int *pointer1, *pointer2; // Fully consistent, two pointers
int* pointer1, pointer2;  // Inconsistent -- because only the first one is a pointer, the second one is an int variable
// The second case is unexpected, and thus prone to errors

2番目のケースに一貫性がないのはなぜですか?たとえばint x,y;、同じ型の2つの変数を宣言しますが、型は宣言で一度しか言及されていません。これにより、前例と予想される動作が作成されます。そしてそれint* pointer1, pointer2;pointer1ポインタとして宣言されているのでそれと矛盾していますが、pointer2はが、整数変数です。明らかにエラーが発生しやすいため、回避する必要があります(タイプではなく、ポインター名の横にアスタリスクを付けることにより)。

ただし、いくつかの例外があります。たとえば、次のように、オブジェクト名の横にアスタリスクを配置できない場合(および配置場所が重要な場合)に、望ましくない結果が得られない場合があります。

MyClass *volatile MyObjName

void test (const char *const p) // const value pointed to by a const pointer

最後に、場合によっては、型名の横にアスタリスクを置く方が間違いなく明確かもしれません。例:

void* ClassName::getItemPtr () {return &item;} // Clear at first sight


2

Cの理論的根拠は、変数をその使用方法で宣言することです。例えば

char *a[100];

それは言う *a[42]だろうchar。そしてa[42]charポインタ。したがってa、charポインタの配列です。

これは、元のコンパイラ作成者が式と宣言に同じパーサーを使用したかったためです。(言語のデザインを選択する理由としては、あまり賢明ではありません)


しかし、書き込みは、それがa とcharのポインタになることchar* a[100]; 推測します。*a[42];chara[42];
yyny

まあ、私たちは皆同じ結論を導き出し、順序だけが変わっています。
Michel Billaud

引用:「* a [42]はcharであり、a [42]はcharポインタになる」と言います。それは逆ではないのですか?
deLock

逆の方法を好む場合a[42]は、charポインタと*a[42]文字です。
ミシェルビロー

2

最初の慣習では、スターをポインター名の側(宣言の右側)に置くことでした

同じルールに従うことができますが、活字側に星をつけても大したことではありません。一貫性が重要であることを忘れないでください。したがって、どちらの側を選択しても、スターは同じ側にあります。


まあ-パーサーはどちらのバリアントも許可するように見えますが、デニスとライナスが右側にあるべきだと言った場合、それは非常に説得力があります。しかし、それでも、私たちはある程度の理論的根拠を欠いており、なぜこれが行われたのかについての説明も欠けています。StackOverflowによると、タブではなくスペースの状況に少し似ています。ただし、タブではなくスペースを使用する人はより多くのお金を稼ぐため、解決されました... :-)
shevy

1

ポインタは、型への修飾子です。アスタリスクが型を変更する方法をよりよく理解するために、右から左に読むことをお勧めします。'int *'は "pointer to int"として読み取ることができます。複数の宣言では、各変数がポインターであることを指定する必要があります。そうしないと、標準変数として作成されます。

1、2、3)テストのタイプは(int *)です。空白は関係ありません。

4、5、6)テストのタイプは(int *)です。Test2の型はintです。この場合も、空白は重要ではありません。


-3

大体の目安として、多くの人々はこれらの概念を次のように理解しているようです。C++では、キーワードまたは識別子の左バインディングによって多くの意味上の意味が導き出されます。

例えば:

int const bla;

constは「int」ワードに適用されます。ポインタのアスタリスクも同様で、それらはそれらの左側のキーワードに適用されます。そして実際の変数名は?うん、それはそれの残りのものによって宣言されています。


1
これは質問の答えにはなりません。さらに悪いことに、それから答えを推測しようとする場合、それはアスタリスクが左側の型にバインドされていることを意味します。右側の単一の変数名にバインドします。
underscore_d 2017年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.