ほとんどの C プログラマーが変数に次のような名前を付けるのはなぜですか。
int *myVariable;
このようではなく:
int* myVariable;
どちらも有効です。アスタリスクは変数名の一部ではなく、型の一部であるように私には思えます。誰でもこのロジックを説明できますか?
ほとんどの C プログラマーが変数に次のような名前を付けるのはなぜですか。
int *myVariable;
このようではなく:
int* myVariable;
どちらも有効です。アスタリスクは変数名の一部ではなく、型の一部であるように私には思えます。誰でもこのロジックを説明できますか?
それらはまったく同等です。ただし、
int *myVariable, myVariable2;
myVariable の型がint*であるのに対し、 myVariable2 の型はintであることは明らかです。の
int* myVariable, myVariable2;
どちらもint*型であることは明らかなように思えるかもしれませんが、 intmyVariable2
型であるため正しくありません。
したがって、最初のプログラミング スタイルはより直感的です。
別の見方をすると、*myVariable
は typeint
であり、これにはある程度の意味があります。
その行の * は、型よりも変数に密接にバインドされているためです。
int* varA, varB; // This is misleading
@Lundin が以下で指摘しているように、const はさらに微妙な点を考慮に入れます。1 行に 1 つの変数を宣言することで、これを完全に回避できます。これは決してあいまいではありません。
int* varA;
int varB;
明確なコードと簡潔なコードのバランスを取るのは難しいです — 何十もの冗長なコードint a;
も良くありません. それでも、デフォルトで 1 行に 1 つの宣言を使用しているため、後でコードを結合することを心配しています。
私はここで手足を出して、変数宣言とパラメーターと戻り値の型の両方について、この質問に対する直接的な答えがある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 の動作なので、参照はこの点で奇妙なものとして際立っています。
それは好みの問題です。
コードを読むと、変数とポインターの区別は 2 番目のケースの方が簡単ですが、一般的な型の変数とポインターの両方を 1 行に入れると混乱を招く可能性があります (プロジェクトのガイドラインでは、それ自体がしばしば推奨されていません)。可読性が低下するため)。
タイプ名の横に対応する記号を付けてポインターを宣言することを好みます。
int* pMyPointer;
このトピックの議論の多くは単純に主観的なものであり、「星は変数名に結合する」という議論は単純です。単なる意見ではないいくつかの議論を次に示します。
忘れられたポインター型修飾子
正式には、「スター」は型にも変数名にも属さず、それ自体がポインターという名前の文法項目の一部です。正式な C 構文 (ISO 9899:2018) は次のとおりです。
(6.7) 宣言:
宣言指定子 init-declarator-list opt;
宣言指定子には型 (およびストレージ) が含まれ、init-declarator-listにはポインターと変数名が含まれます。この宣言子リストの構文をさらに分析すると、次のようになります。
(6.7.6)宣言子:
ポインタopt直接宣言子
...
(6.7.6)ポインタ:
*
型修飾子リストopt
*
型修飾子リストopt ポインタ
宣言子が宣言全体である場合、直接宣言子は識別子 (変数名) であり、ポインターはスターであり、ポインター自体に属するオプションの型修飾子リストが続きます。
「星は変数に属する」というさまざまなスタイルの議論に一貫性がないのは、これらのポインター型修飾子を忘れていることです。int* const x
、int *const x
またはint*const x
?
とint *const a, b;
の型は何ですか?「星は変光星に属している」ということはもはや明らかではありません。むしろ、 はどこに属しているのかを考え始めるでしょう。a
b
const
スターがポインター型修飾子に属しているという健全な議論を間違いなく行うことができますが、それ以上のことはできません。
int *a
ポインターの型修飾子リストは、スタイルを使用しているユーザーに問題を引き起こす可能性があります。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 行で宣言すると、ほとんどの場合、コードがより明確になります。
したがって、プログラマーが混乱するという議論int* a, b
は良くありません。問題の根本は、複数の宣言子の使用であり、. の配置ではありません*
。スタイルに関係なく、代わりに次のように記述する必要があります。
int* a; // or int *a
int b;
int* a
もう 1 つの健全だが主観的な議論は、 isの型が与えられた場合、a
疑問の余地がないint*
ため、星は型修飾子に属するというものです。
しかし基本的に私の結論は、ここに投稿された議論の多くは主観的でナイーブだということです。どちらのスタイルについても正当な議論をすることはできません - それは本当に主観的な個人的な好みの問題です.
1) CERT-C DCL04-C .
次のような宣言があると、より理にかなっているからです。
int *a, *b;
1 行で複数のポインターを宣言する場合は、int* a, * b;
"a" を整数へのポインターとしてより直感的に宣言し、同様に "b" を宣言するときにスタイルを混在させない方を好みます。誰かが言ったように、とにかく同じステートメントで 2 つの異なる型を宣言しません。