C/C++ で「何かへのポインタ」を記述する「良い」方法はありますか?
私は書いていましたが、の型が「charへのポインタ」void foo( char *str );
であるため、非常に非論理的であることが時々あります。
ポインタの書き方にルールはありますか?str
*
char*str;
char* str;
char *str;
char * str;
C/C++ で「何かへのポインタ」を記述する「良い」方法はありますか?
私は書いていましたが、の型が「charへのポインタ」void foo( char *str );
であるため、非常に非論理的であることが時々あります。
ポインタの書き方にルールはありますか?str
*
char*str;
char* str;
char *str;
char * str;
厳密なルールはありませんが、 が*
変数にアタッチされることに注意してください。
char *str1, *str2; // str1 and str2 are pointers
char* str1, str2; // str1 is a pointer, str2 is a char
同様に行うのが好きな人もいchar * str1
ますが、それはあなたまたはあなたの会社のコーディング標準次第です。
一般的なCの規則は、を書くことT *p
ですが、一般的なC ++の規則は、を書くことT* p
です。T (*p)
両方とも;として解析します。これ*
は宣言子の一部であり、型指定子ではありません。どちらの方法でも記述できるのは、純粋にポインタ宣言構文の偶然です。
C(および拡張によりC ++)宣言構文は、式中心です。IOW、宣言の形式は、コード内の同じタイプの式の形式と一致する必要があります。
たとえば、へのポインタがint
あり、その整数値にアクセスしたいとします。そのためには、次のように、間接演算子を使用してポインターを間接参照します。*
x = *p;
式 のタイプ*p
はint
;です。したがって、の宣言は次のようにp
なります。
int *p
の整数p
は型指定子によって提供されますint
が、のポインタp
は宣言子によって提供され*p
ます。
もう少し複雑な例として、の配列へのポインターがあり、ポインターを介して配列の'番目の要素にfloat
ある浮動小数点値にアクセスしたいとします。i
配列ポインターを逆参照し、結果に添え字を付けます。
f = (*ap)[i];
式 の型は(*ap)[i]
、float
であるため、配列ポインタの宣言は次のようになります。
float (*ap)[N];
のfloat-nessはap
型指定子によって提供されますfloat
が、pointer-nessとarray-nessは宣言子によって提供され(*ap)[N]
ます。この場合、は識別子に明示的にバインドする*
必要があることに注意してください。式と宣言の構文の両方で単項よりも優先順位が高いため、「の配列へのポインタ」ではなく、「へのポインタの配列」として解析されます。私はあなたがそれを次のように書くことができると思います[]
*
float* ap[N]
float *(ap[N])
float
float
float(* ap)[N];
しかし、ポイントが何であるかはわかりません。タイプがap
明確になるわけではありません。
int
さらに良いことに、 :へのポインタの配列へのポインタを返す関数へのポインタはどうですか?
int *(*(*f)())[N];
この場合も、少なくとも2つの*
演算子を宣言子に明示的にバインドする必要があります。*
最後を型指定子にバインドします。
int* (*(*f)())[N];
混乱した思考IMOを示しているだけです。
私はそれを自分のC++コードで使用し、なぜそれが普及したのかは理解していますが、T* p
規則の背後にある理由で私が抱えている問題は、それが最も単純なポインター宣言の外では適用されないことです。 CおよびC++宣言構文の単純化された間違った見方。はい、タイプp
は「Tへのポインター」ですが、言語文法に関する限り*
、タイプ指定子ではなく宣言子にバインドされるという事実は変わりません。
別のケースでは、タイプa
が「TのN要素配列」の場合、次のように記述しません。
T[N] a;
明らかに、文法はそれを許可していません。繰り返しますが、この場合、議論は当てはまりません。
編集
Steveがコメントで指摘しているように、typedefを使用して複雑さの一部を隠すことができます。たとえば、書き直すことができます
int *(*(*f)())[N];
のようなものとして
typedef int *iptrarr[N]; // iptrarr is an array of pointer to int
typedef iptrarr *arrptrfunc(); // arrptrfunc is a function returning
// a pointer to iptrarr
arrptrfunc *f; // f is a pointer to arrptrfunc
これで、としてT* p
宣言して、規則をきれいに適用できます。typedefから、式でどのように使用されるのか、またはタイプのオブジェクトをどのように使用するのかが必ずしも明確ではないため、私は個人的にこの方法を好むわけではありません。typedefされていないバージョンは見苦しくて読みにくいかもしれませんが、少なくとも、事前に知っておく必要のあるすべてのことを教えてくれます。すべてのtypedefを掘り下げる必要はありません。 f
arrptrfunc* f
f
arrptrfunc
「良い方法」は
(多分)この順番で。
これに正解も不正解もありません。重要なことは、コーディング標準を 1 つ選び、それに固執することです。
そうは言っても、タイプは「charへのポインター」であるため、 * は変数名ではなくタイプに属していると個人的に信じています。変数名はポインターではありません。
これは、変数の宣言方法の一般的なパターンに大きく影響されると思います。
たとえば、1行に1つの変数のみを宣言する傾向があります。このようにして、変数の使用方法を思い出させるコメントを追加できます。
ただし、同じタイプの複数の変数を1行で宣言することが実際的である場合があります。このような状況では、私の個人的なコーディング規則は、ポインターを非ポインターと同じ行に宣言しないことです。それらを混ぜるとエラーの原因になることがあるので、混ぜないようにして「間違い」を見やすくするようにしています。
最初のガイドラインに従っている限り、一貫している限り、ポインタをどのように宣言するかはそれほど重要ではないことがわかります。
ただし、2番目のガイドラインを使用し、同じ行に複数のポインターを宣言すると、次のスタイルが最も有益で明確であることがわかります(もちろん他の人は同意しない場合があります)...
char *ptr1, *ptr2, *ptr3;
*とポインタ名の間にスペースを入れないことで、2番目のガイドラインに違反しているかどうかを簡単に特定できます。
さて、2つの個人的なスタイルガイドラインの間で一貫性を保ちたい場合、1行に1つのポインターのみを宣言するときは、...を使用します。
char *ptr;
とにかく、それが私がやっていることをする理由の一部の私の論理的根拠です。お役に立てれば。