次の宣言の違いは何ですか。
int* arr1[8];
int (*arr2)[8];
int *(arr3[8]);
より複雑な宣言を理解するための一般的なルールは何ですか?
次の宣言の違いは何ですか。
int* arr1[8];
int (*arr2)[8];
int *(arr3[8]);
より複雑な宣言を理解するための一般的なルールは何ですか?
int* arr[8]; // An array of int pointers.
int (*arr)[8]; // A pointer to an array of integers
3つ目は1つ目と同じです。
一般的なルールは、演算子の優先順位です。関数ポインタが登場するにつれて、さらに複雑になる可能性があります。
K&Rの提案に従って、 cdeclプログラムを使用します。
$ cdecl
Type `help' or `?' for help
cdecl> explain int* arr1[8];
declare arr1 as array 8 of pointer to int
cdecl> explain int (*arr2)[8]
declare arr2 as pointer to array 8 of int
cdecl> explain int *(arr3[8])
declare arr3 as array 8 of pointer to int
cdecl>
それは逆にも機能します。
cdecl> declare x as pointer to function(void) returning pointer to float
float *(*x)(void )
正式名称かどうかはわかりませんが、Right-LeftThingy(TM)と呼んでいます。
変数から始めて、右、左、右に移動します...など。
int* arr1[8];
arr1
整数への8つのポインタの配列です。
int (*arr2)[8];
arr2
は、8つの整数の配列へのポインター(括弧は左右をブロックします)です。
int *(arr3[8]);
arr3
整数への8つのポインタの配列です。
これは、複雑な宣言に役立つはずです。
int *a[4]; // Array of 4 pointers to int
int (*a)[4]; //a is a pointer to an integer array of size 4
int (*a[8])[5]; //a is an array of pointers to integer array of size 5
最後の2つの答えは、Cの黄金律から差し引くこともできます。
宣言は使用に続きます。
int (*arr2)[8];
間接参照するとどうなりますarr2
か?8つの整数の配列を取得します。
int *(arr3[8]);
から要素を取得するとどうなりますarr3
か?整数へのポインタを取得します。
これは、関数へのポインターを処理するときにも役立ちます。sigjuiceの例をとると:
float *(*x)(void )
間接参照するとどうなりますx
か?引数なしで呼び出すことができる関数を取得します。あなたがそれを呼ぶとどうなりますか?へのポインタを返しますfloat
。
ただし、演算子の優先順位は常に注意が必要です。ただし、宣言は使用に続くため、括弧の使用も実際には混乱を招く可能性があります。少なくとも、私には、直感的arr2
にはintへの8つのポインターの配列のように見えますが、実際にはその逆です。慣れるのに少し時間がかかります。あなたが私に尋ねれば、これらの宣言に常にコメントを追加するのに十分な理由:)
編集:例
ちなみに、私は次の状況に遭遇しました。静的行列を持ち、ポインター演算を使用して行ポインターが範囲外かどうかを確認する関数。例:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define NUM_ELEM(ar) (sizeof(ar) / sizeof((ar)[0]))
int *
put_off(const int newrow[2])
{
static int mymatrix[3][2];
static int (*rowp)[2] = mymatrix;
int (* const border)[] = mymatrix + NUM_ELEM(mymatrix);
memcpy(rowp, newrow, sizeof(*rowp));
rowp += 1;
if (rowp == border) {
rowp = mymatrix;
}
return *rowp;
}
int
main(int argc, char *argv[])
{
int i = 0;
int row[2] = {0, 1};
int *rout;
for (i = 0; i < 6; i++) {
row[0] = i;
row[1] += i;
rout = put_off(row);
printf("%d (%p): [%d, %d]\n", i, (void *) rout, rout[0], rout[1]);
}
return 0;
}
出力:
0 (0x804a02c): [0, 0]
1 (0x804a034): [0, 0]
2 (0x804a024): [0, 1]
3 (0x804a02c): [1, 2]
4 (0x804a034): [2, 4]
5 (0x804a024): [3, 7]
borderの値は変更されないため、コンパイラーはそれを最適化できることに注意してください。これは、最初に使用したいものとは異なります。const int (*border)[3]
::変数が存在する限り値を変更しない、3つの整数の配列へのポインターとしてborderを宣言します。ただし、そのポインタはいつでも他のそのような配列を指すことができます。代わりに、引数にそのような動作が必要です(この関数はこれらの整数を変更しないため)。宣言は使用に続きます。
(ps:このサンプルを自由に改善してください!)
typedef int (*PointerToIntArray)[];
typedef int *ArrayOfIntPointers[];
経験則として、右の単項演算子([]
、()
など)は左の演算子よりも優先されます。したがって、int *(*ptr)()[];
intへのポインターの配列を返す関数を指すポインターになります(括弧から抜けたら、できるだけ早く適切な演算子を取得してください)
簡単なルールが使えると思います。
example int * (*ptr)()[];
start from ptr
"ptr
は"右に行く..その")"へのポインタです"("出て右に行く "()"なので"引数をとらない関数に"左に行く"そしてポインタを返す"行く右「配列へ」「整数の左へ」
これが私がそれをどのように解釈するかです:
int *something[n];
優先順位に関する注意:配列添え字演算子(
[]
)は、間接参照演算子()よりも優先順位が高くなります*
。
したがって、ここでは[]
beforeを適用*
し、ステートメントを次と同等にします。
int *(something[i]);
宣言がどのように意味をなすかについての注意:
int num
meansnum
は、、int
またはint *ptr
meansint (*ptr)
、(の値ptr
)は、int
へptr
のポインタになりint
ます。
これは、((何かのi番目のインデックスの値)の値)は整数であると読み取ることができます。したがって、(何かのi番目のインデックスの値)は(整数ポインター)であり、何かを整数ポインターの配列にします。
2つ目は、
int (*something)[n];
このステートメントを理解するには、次の事実に精通している必要があります。
配列のポインタ表現に関する注意:
somethingElse[i]
と同等です*(somethingElse + i)
したがって、に置き換えるsomethingElse
と、宣言ごとの整数である、が(*something)
得られます。*(*something + i)
したがって、配列を指定すると、 (配列へのポインタ)と(*something)
同等のものになります。
Cで複雑な型を読み取る方法を説明する興味深いWebサイトは次のとおりです。http: //www.unixwiz.net/techtips/reading-cdecl.html
2番目の宣言は多くの人を混乱させていると思います。これを理解する簡単な方法があります。
整数の配列、すなわちint B[8]
。
Bを指す変数Aも用意しましょう。ここで、Aの値はB、つまり(*A) == B
です。したがって、Aは整数の配列を指します。あなたの質問では、arrはAに似ています。
同様に、ではint* (*C) [8]
、Cは整数へのポインタの配列へのポインタです。
int *arr1[5]
この宣言でarr1
は、は整数への5つのポインターの配列です。理由:角かっこは*(逆参照演算子)よりも優先されます。このタイプでは、行数は固定されていますが(ここでは5)、列数は可変です。
int (*arr2)[5]
この宣言でarr2
は、は5つの要素の整数配列へのポインタです。理由:ここでは、()角かっこは[]よりも優先されます。このタイプでは、行数は可変ですが、列数は固定されています(ここでは5)。
整数へのポインタでは、ポインタがインクリメントされると、次の整数になります。
ポインタの配列でポインタがインクリメントされると、次の配列にジャンプします