私はポインター記号*に苦労しています。宣言と式の両方での使用方法が非常にわかりにくいと感じています。
例えば:
int *i; // i is a pointer to an int
しかし、構文の背後にあるロジックは何ですか? iの直前の*はどういう意味ですか?次の例を見てみましょう。私が間違っている場所を修正してください:
char **s;
char *(*s); // added parentheses to highlight precedence
そして、これは私が道を失うところです。括弧間の* sは、次のことを意味します。しかし、何へのポインタ?そして、括弧の外側の*はどういう意味ですか:sが指しているものへのポインター?
したがって、これの意味は次のとおりです。sが指しているものを指すポインターは、charを指すポインターですか?
私は迷っています。宣言と式で*記号の解釈が異なりますか?もしそうなら、それはどのように異なって解釈されますか?どこがおかしいの?
Cでの宣言のルールは、使用する方法で宣言することです。
char *p
は、必要な*p
文字を取得するには、
char **p
は、必要な**p
文字を取得します。
このようにしてください:
int *i
は、iが指す値が整数であることを意味します。
char **p
は、pがそれ自体がcharへのポインターであるポインターであることを意味します。
int i; //i is an int.
int *i; //i is a pointer to an int
int **i;//i is a pointer to a pointer to an int.
宣言と式で*記号の解釈が異なりますか?
はい。それらは完全に異なっています。宣言では*はポインタの宣言に使用されます。式では、単項*を使用してポインターを逆参照します(または2項乗算演算子として)
いくつかの例:
int i = 10; //i is an int, it has allocated storage to store an int.
int *k; // k is an uninitialized pointer to an int.
//It does not store an int, but a pointer to one.
k = &i; // make k point to i. We take the address of i and store it in k
int j = *k; //here we dereference the k pointer to get at the int value it points
//to. As it points to i, *k will get the value 10 and store it in j
Cの宣言は式中心です。つまり、宣言の形式は実行可能コードの式の形式と一致する必要があります。
たとえば、p
という名前の整数へのポインターがあるとします。 p
が指す整数値にアクセスしたいので、dereferenceのように、次のようにします。
x = *p;
expression*p
のタイプはint
;です。したがって、p
の宣言は次の形式を取ります。
int *p;
この宣言では、int
は型指定子であり、*p
は宣言子です。宣言子は、型指定子によって提供されない追加の型情報とともに、宣言されるオブジェクトの名前(p
)を導入します。この場合、追加の型情報は、p
がポインター型であることです。宣言は、「p
はint
へのポインター型」または「p
はint
型へのポインター」のいずれかとして読み取ることができます。私は2番目の形式を使用することを好み、他の形式は最初の形式を好みます。
この宣言をint *p;
またはint* p;
のいずれかとして記述できるのは、CおよびC++構文の偶然です。どちらの場合も、int (*p);
として解析されます。つまり、*
は、型指定子ではなく、常に変数名に関連付けられます。
int
へのポインタの配列があり、配列のi番目の要素が指す値にアクセスしたいとします。次のように、配列に添え字を付け、結果を逆参照します。
x = *ap[i]; // parsed as *(ap[i]), since subscript has higher precedence
// than dereference.
繰り返しますが、expression*ap[i]
の型はint
なので、ap
の宣言は
int *ap[N];
ここで、宣言子*ap[N]
は、ap
がint
へのポインターの配列であることを示します。
そして、ポイントを家に戻すために、int
へのポインターへのポインターがあり、その値にアクセスしたいとします。再び、ポインターを参照し、その結果を逆参照して整数値を取得します。
x = **pp; // *pp deferences pp, then **pp dereferences the result of *pp
式**pp
の型はint
であるため、宣言は
int **pp;
宣言子**pp
は、pp
がint
への別のポインターへのポインターであることを示します。
通常、関数に渡すポインター値を変更する場合、次のような二重間接参照が多く表示されます。
void openAndInit(FILE **p)
{
*p = fopen("AFile.txt", "r");
// do other stuff
}
int main(void)
{
FILE *f = NULL;
...
openAndInit(&f);
...
}
この場合、関数でf
の値を更新する必要があります。そのためには、f
へのポインターを渡す必要があります。 f
は既にポインター型(FILE *
)であるため、FILE *
へのポインターを渡すことを意味します。したがって、p
の宣言はFILE **p
として行われます。 openAndInit
の-expression*p
は、f
のmain
式と同じオブジェクトを参照することに注意してください。
宣言と式の両方で、[]
と()
の両方の優先順位は、単項*
よりも高くなっています。たとえば、*ap[i]
は*(ap[i])
;と解釈されます。式ap[i]
はポインター型であり、*
はそのポインターを逆参照します。したがって、ap
はポインターの配列です。 配列へのポインタを宣言する場合は、次のように*
を配列名で明示的にグループ化する必要があります。
int (*pa)[N]; // pa is a pointer to an N-element array of int
配列内の値にアクセスする場合は、添え字を適用する前にpa
に従う必要があります。
x = (*pa)[i];
同様に関数:
int *f(); // f is a function that returns a pointer to int
...
x = *f(); // we must dereference the result of f() to get the int value
int (*f)(); // f is a pointer to a function that returns an int
...
x = (*f)(); // we must dereference f and execute the result to get the int value
複雑な宣言子を解析するための私のお気に入りの方法は、 clockwise-spiral rule です。
基本的には、識別子から開始し、時計回りのスパイラルに従います。リンクを参照して、使用方法を確認してください。
この記事で言及されていない2つのこと:
1-型指定子(int、charなど)を宣言子から分離し、宣言子を解析してから型指定子を追加する必要があります。
2-配列を示す角括弧に遭遇した場合は、次の角括弧(ある場合)も必ず読んでください。
_int * i
_は、iがintへのポインターであることを意味します(逆読み、*ポインターとして読み取り)。 _char **p
_およびchar *(*p)
は両方とも、charへのポインターへのポインターを意味します。
他の例をいくつか示します
_int* a[3]
_ // aは、intへの3つのポインターの配列です
int (*a)[3]
// aは3つのintの配列へのポインタです
質問には答えがあります。
実際、ポインターへのポインターを示すために二重星が使用されます。
宣言の*は、変数が他の変数/定数へのポインターであることを意味します。そのタイプの変数のアドレスを保持できることを意味します。例えば:char *c;
はcがcharのアドレスを保持できることを意味し、int *b
はbがintのアドレスを保持できることを意味します。ポインター演算ではpointer + 1
は実際にはpointer + (1 * sizeof(*pointer))
。
式の*は「アドレスに格納されている値」を意味するため、c
が特定の文字へのポインターである場合、*c
は特定の文字です。
char *(*s);
は、sがcharへのポインタへのポインタであることを意味します。したがって、sはcharのアドレスを保持しませんが、charのアドレスを保持する変数のアドレスを保持します。
&a
を宣言することは、*i
を指すことを意味します。結局、それは*int
へのポインターです。整数は*i
を指します。しかし、j = *k
がポインターへのポインターであると考えると、&k
はk
の値になり、k
は*int
へのポインターになります。
ここにちょっとした情報があります
variable pointer
declaring &a p
reading/ a *p
processing