回答:
それらはまったく同じです。ただし、
int *myVariable, myVariable2;
myVariableの型がint *であるのは明らかなようですが、myVariable2の型はintです。に
int* myVariable, myVariable2;
どちらもint *型であることは明白に思えるかもしれませんが、それはintmyVariable2
型なので正しくありません。
したがって、最初のプログラミングスタイルはより直感的です。
int* someVar
個人的なプロジェクトに固執しています。それはもっと理にかなっています。
int[10] x
。これは単にCの構文ではありません。文法はとしてint (*x)
ではなく、として明示的に解析する(int *) x
ため、アスタリスクを左側に配置することは単に誤解を招くものであり、C宣言構文の誤解に基づいています。
int* myVariable; int myVariable2;
代わりに行っても、まったく混乱はありません。
別の見方をすると、*myVariable
はタイプint
であり、ある程度の意味があります。
myVariable
NULLにすることもできます。その場合*myVariable
、セグメンテーション違反が発生しますが、タイプNULLはありません。
int x = 5; int *pointer = &x;
それは、int *pointer
をpointer
それ自体ではなく、ある値に設定することを示唆しているためです。
その行の*は、型よりも変数に密接にバインドしているためです。
int* varA, varB; // This is misleading
@Lundinが以下に指摘するように、constはさらに検討する必要がある微妙な点を追加します。行ごとに1つの変数を宣言することで、これを完全に回避できます。
int* varA;
int varB;
明確なコードと簡潔なコードのバランスをとるのは難しく、12行の冗長な行int a;
も適切ではありません。それでも、私はデフォルトで1行につき1つの宣言を行い、後でコードを結合することについて心配しています。
int *const a, b;
。*「バインド」はどこにありますか?の型a
はint* const
なので、*が型自体の一部である場合、*は変数に属しているとどうして言えますか?
これまで誰もここで触れていないことは、このアスタリスクは実際にはCの「逆参照演算子」であるということです。
*a = 10;
上記の行は、私が割り当てたいという意味ではありません10
しa
、それは私が割り当てること10
何でもメモリ位置へa
のポイント。そして、私は誰も書いているのを見たことがありません
* a = 10;
あなたはいますか?したがって、間接参照演算子は、ほとんど常にスペースなしで記述されます。これはおそらく、複数行にまたがる乗算と区別するためです。
x = a * b * c * d
* e * f * g;
ここに *e
は誤解を招くでしょうね。
さて、次の行は実際にはどういう意味ですか?
int *a;
ほとんどの人は言うでしょう:
それは値a
へのポインタであることを意味しint
ます。
これは技術的に正しく、ほとんどの人はそれをそのように見たり読んだりすることを好み、それが現代のC標準がそれを定義する方法です(C言語自体がすべてのANSIおよびISO標準に先行していることに注意してください)。しかし、それを見る唯一の方法ではありません。この行は次のように読むこともできます。
の逆参照された値のa
タイプはint
です。
したがって、実際には、この宣言のアスタリスクは、その配置を説明する逆参照演算子としても見なされます。そして、それa
はポインタが実際にまったく宣言されていないことであり、実際に逆参照できるのはポインタだけであるという事実によって暗黙的です。
C標準では、*
演算子に対して2つの意味のみを定義しています。
そして、間接参照は単一の意味に過ぎず、ポインターを宣言するための特別な意味はありません。間接参照だけがあり、これは間接参照操作が行うものであり、間接アクセスを実行するので、int *a;
このようなステートメント内でも間接アクセス(*
手段したがって、上記の2番目のステートメントは、最初のステートメントよりもはるかに標準に近いものです。
int a, *b, (*c)();
「次のオブジェクトを次のように宣言しますint
:オブジェクトa
、が指すオブジェクトb
、およびが指す関数から返されたオブジェクト」のようなものとして読みますc
。
*
int *a;
a
*
(それがないように、合計式にのみ意味を与える*
表現内で!)。「int a;」*
a
と書いてあります ポインタを宣言しますが、それがそうであると主張することは決してありませんが、に与えられた意味がなければ、それを*の逆参照された値はint であると読み取りますが、それは同じ事実上の意味を持つので、完全に有効です。6.7.6.1で書かれたものは何も、実際には何もそのステートメントに矛盾しません。
int *a;
式ではなく宣言です。a
によって逆参照されませんint *a;
。 処理さa
れている時点で*
はまだ存在していません。int *a = NULL;
nullポインターを逆参照するため、バグだと思いますか?
私はここで外に出て、変数の宣言とパラメータと戻り値の型の両方について、この質問に対する直接の回答があると言います。つまり、アスタリスクは名前の隣に置く必要がありますint *myVariable;
。理由を理解するには、Cで他のタイプのシンボルを宣言する方法を見てください。
int my_function(int arg);
関数の場合。
float my_array[3]
配列の場合。
呼ばれる一般的なパターン、宣言は使用を以下のは、シンボルのタイプは名前の前の部分に分割され、かつ部品ということで周りの構文模倣名の前後名、およびこれらの部品は、あなたが取得するために使用します左側のタイプの値:
int a_return_value = my_function(729);
float an_element = my_array[2];
そして、: int copy_of_value = *myVariable;
。
C ++は、参照を使用する作業でスパナをスローします。これは、参照を使用する時点での構文が値型の構文と同じであるため、C ++がCに対して異なるアプローチをとっていると主張できます。一方、C ++は同じポインタの場合のCの動作なので、参照は実際にはこの点で奇妙なものとして立っています。
それは好みの問題です。
2番目のケースでは、コードを読み取るときに変数とポインターを区別するのが簡単ですが、共通の型の変数とポインターの両方を1行に入れると混乱を招く可能性があります(プロジェクトガイドラインでは、これ自体が推奨されていないことが多く、読みやすさが低下するため)。
タイプ名の横に対応する記号を付けてポインタを宣言することを好みます。たとえば、
int* pMyPointer;
偉大な達人はかつて「コンパイラのように読んでください、あなたはそうしなければなりません」と言っていました。
http://www.drdobbs.com/conversationsa-midsummer-nights-madness/184403835
これはconst配置のトピックに関するものでしたが、ここでも同じルールが適用されます。
コンパイラはそれを次のように読み取ります。
int (*a);
ではない:
(int*) a;
変数の横に星を付ける習慣をつけると、宣言が読みやすくなります。また、次のような目障りを回避します。
int* a[10];
-編集-
として解析されたと私が言っていることを正確に説明すると、それは、の優先順位が高いために、式ではがよりもより強く結合するのと同じように、よりもより強く結合int (*a)
することを意味します。*
a
int
4 + 3 * 7
3
7
4
*
asciiアートの謝罪により、構文解析のためのASTの概要は、int *a
おおよそ次のようになります。
Declaration
/ \
/ \
Declaration- Init-
Secifiers Declarator-
| List
| |
| ...
"int" |
Declarator
/ \
/ ...
Pointer \
| Identifier
| |
"*" |
"a"
明確に示されているように、共通の祖先がであるため、*
はより緊密にバインドしますa
がDeclarator
、をDeclaration
含む共通の祖先を見つけるには、ツリーまで上に移動する必要がありますint
。
(int*) a
。
int
この場合。ステップ2.型の装飾を含む宣言を読みます。 *a
この場合。ステップ3次の文字を読みます。コンマの場合は、それを消費してステップ2に戻ります。セミコロンの場合は停止します。それ以外の場合は構文エラーをスローします。...
int* a, b;
ペアのポインターを作成して取得することができます。私が主張しているの*
は、宣言が変数の「基本型」を形成する型ではなく、変数にバインドされて解析されるということです。これは、typedefがtypedef int *iptr;
iptr a, b;
いくつかのポインタを作成できるように導入された理由の一部でもあります。typedefを使用すると、*
をにバインドできますint
。
Declaration-Specifier
「装飾」と「結合」して、Declarator
各変数の最終型に到達します。ただし、デコレーション指定子に装飾を「移動」しないint a[10], b;
と、完全にばかげた結果が生成され、int *a, b[10];
として解析されint
*a
,
b[10]
;
ます。それを説明する他の方法はありません。
int*a
最終的には「a
型があるint*
」としてコンパイラに読み込まれます。それが私の元のコメントで私が言ったことでした。
次のような宣言がある方がより意味があるからです。
int *a, *b;
int* a, b;
作成b
することを指定できたはずint
です。しかし、そうではありませんでした。そして正当な理由があります。提案されたシステムでb
は、次の宣言のタイプは何int* a[10], b;
ですか。
1行で複数のポインターを宣言するint* a, * b;
ために、整数へのポインターとしてより直感的に "a"を宣言し、同様に "b"を宣言するときにスタイルを混在させないことを好みます。誰かが言ったように、私はとにかく同じ声明で2つの異なる型を宣言しないでしょう。
好む人 int* x;
タイプを左側に、識別子(名前)を右側にした架空の世界にコードを強制しようとするます。
私は「架空」と言います。
CおよびC ++では、一般的なケースでは、宣言された識別子は型情報で囲まれます。
クレイジーに聞こえるかもしれませんが、あなたはそれが本当であることを知っています。ここではいくつかの例を示します。
int main(int argc, char *argv[])
とmain
は、int
とへのポインタの配列を取り、char
を返す関数であることを意味しますint
。つまり、タイプ情報のほとんどは右側にあります。一部の人々は、関数宣言は何らかの形で「特別」であるため、カウントされないと考えています。では、変数を試してみましょう。
void (*fn)(int)
手段はfn
取る関数へのポインタであるint
と何も返しません。
int a[10]
「a」を10 int
秒の配列として宣言します。
pixel bitmap[height][width]
。
明らかに、私は私の権利を主張するために右側に多くのタイプ情報を持っている例を厳選しました。のように、型のほとんど(すべてではないにしても)が左側にある宣言はたくさんありますstruct { int x; int y; } center
。
この宣言構文は、宣言に使用法を反映させるというK&Rの要望から生まれました。単純な宣言を読むことは直感的であり、より複雑な宣言を読むことは、右から左への規則を学習することによって習得できます(スパイラル規則または右左規則だけを呼び出すこともあります)。
Cは非常に単純なので、多くのCプログラマーがこのスタイルを採用し、単純な宣言をとして記述しint *p
ます。
C ++では、構文が少し複雑になり(クラス、参照、テンプレート、列挙型クラスを使用)、その複雑さへの反応として、多くの宣言で型を識別子から分離するためのより多くの努力が見られます。言い換えると、int* p
C ++コードの大規模なスワスをチェックアウトすると、-style宣言がさらに表示される可能性があります。
どちらの言語でも、(1)同じステートメントで複数の変数を宣言しないこと、および(2)s(または、皮肉にもエイリアスを置くエイリアス宣言)を使用することにより、常に変数宣言の左側に型を持つことができます。タイプの左側の識別子)。例えば:typedef
typedef int array_of_10_ints[10];
array_of_10_ints a;
(*fn)
でfn
はなく、関連付けられたポインタを保持するためです。
このトピックの議論の多くは明白な主観的であり、「星は変数名に結びついている」という議論は素朴です。以下は、意見だけではないいくつかの議論です。
忘れられたポインタ型修飾子
正式には、「スター」は型にも変数名にも属していません。ポインタという名前の独自の文法項目の一部です。正式なC構文(ISO 9899:2018)は次のとおりです。
(6.7)宣言:
宣言指定子 init-declarator-list opt;
ここで、declaration-specifiersにはタイプ(およびストレージ)が含まれ、init-declarator-listにはポインターと変数名が含まれます。この宣言リストの構文をさらに詳しく分析すると、次のようになります。
(6.7.6)declarator:
pointer opt direct-declarator
...
(6.7.6)pointer:
*
type-qualifier-list opt
*
type-qualifier-list opt pointer
宣言子が宣言全体である場合、直接宣言子は識別子(変数名)であり、ポインターは星形で、その後にポインター自体に属するオプションの型修飾子リストが続きます。
「スターは変数に属している」についてのさまざまなスタイルの引数に一貫性がないのは、これらのポインター型修飾子について忘れていたことです。int* const x
、int *const x
またはint*const x
?
考えてint *const a, b;
、のタイプa
とはb
何ですか?「星は変数に属している」ことはもはや明白ではありません。むしろ、人はどこを熟考し始めますconst
属しているます。
スターはポインター型修飾子に属しているが、それ以上ではないことを間違いなく主張できます。
ポインタの型修飾子リストは、int *a
スタイルを使用する人に問題を引き起こす可能性があります。内部でポインターを使用しtypedef
(非常に悪い習慣です!)、「スターは変数名に属している」と考える人は、次の非常に微妙なバグを書く傾向があります。
/*** bad code, don't do this ***/
typedef int *bad_idea_t;
...
void func (const bad_idea_t *foo);
これはきれいにコンパイルされます。ここで、コードがconstで正しいと考えるかもしれません。そうじゃない!このコードは誤って偽のconstの正しさです。
のタイプfoo
は実際にはint*const*
-最も外側のポインターは読み取り専用にされており、データを指していません。この関数の中で、**foo = n;
、呼び出し元の変数値を変更します。
これは、式const bad_idea_t *foo
で*
が変数名に属していないためです!疑似コードでは、このパラメーター宣言はとしてconst (bad_idea_t *) foo
ではなくとして読み取られ(const bad_idea_t) *foo
ます。この場合、スターは非表示のポインター型に属しています。型はポインターであり、const修飾ポインターは次のように記述されます。*const
ます。
しかし、上記の例の問題の根本はtypedef
、*
スタイルではなくaの後ろにポインタを隠すことです。
1行での複数の変数の宣言について
1行で複数の変数を宣言することは、悪い習慣として広く認識されています1)。CERT-Cは、次のようにうまくまとめています。
DCL04-C。1つの宣言で複数の変数を宣言しないでください
英語を読むだけで、常識では宣言は1つの宣言であることに同意します。
そして、変数がポインタであるかどうかは問題ではありません。各変数を1行で宣言すると、ほとんどすべての場合でコードが明確になります。
したがって、プログラマーが混乱することについての議論int* a, b
は悪いことです。問題の根本は、の配置ではなく、複数の宣言子の使用です*
。スタイルに関係なく、代わりにこれを書く必要があります:
int* a; // or int *a
int b;
別の健全だが主観的な議論はint* a
、型がa
疑わしいint*
ので、スターは型修飾子に属しているということです。
しかし、基本的に私の結論は、ここに投稿された議論の多くは主観的で素朴なものでしかないということです。どちらのスタイルについても、有効な議論をすることはできません。それは、主観的な個人の好みの問題です。
1) CERT-C DCL04-C。
typedef int *bad_idea_t;
void func(const bad_idea_t bar);
、偉大な預言者ダンサックスが「const
意味的に意味を変えずに、常にできるだけ右側に配置する場合」のトピックに関する短いセクションがあります。これは完全に停止します。問題になる。また、const
宣言を読みやすくすることができます。「constという単語の右側にあるものはすべてconstであり、左側にあるものはすべてその型です。」これは、宣言内のすべてのconstに適用されます。試してみるint const * * const x;
検討する
int *x = new int();
これは変換されません
int *x;
*x = new int();
それは実際に
int *x;
x = new int();
これにより、int *x
表記に多少の一貫性がなくなります。
new
C ++演算子です。彼の質問には「C ++」ではなく「C」というタグが付いています。
typedefs
、それは不必要な複雑さ、IMHOを追加します。