次の宣言の違いは何ですか:
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番目のものは最初のものと同じです。
一般的な規則は 演算子の優先順位 です。関数ポインタが登場するにつれて、さらに複雑になる可能性があります。
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-Left Thingy(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] [。 [1、2] 4(0x804a034):[2、4] 5(0x804a024):[3、7]
Borderの値は決して変化しないので、コンパイラはそれを最適化することができます。これは、最初に使用したいものとは異なります。const int (*border)[3]
:変数が存在する限り値を変更しない3つの整数の配列へのポインタとしてborderを宣言します。しかし、そのポインタはいつでも他のそのような配列を指すことができます。代わりに、このような振る舞いを引数に求めます(この関数はこれらの整数のいずれも変更しないため)。宣言は使用後に続きます。
(p.s。:このサンプルを自由に修正してください!)
typedef int (*PointerToIntArray)[];
typedef int *ArrayOfIntPointers[];
経験則として、右単項演算子([]
、()
など)は左演算子よりも優先されます。そのため、int *(*ptr)()[];
はintへのポインタの配列を返す関数を指すポインタになります(括弧から抜けたらすぐに正しい演算子を取得してください)。
これはCで複雑な型を読む方法を説明する興味深いウェブサイトです: http://www.unixwiz.net/techtips/reading-cdecl.html
これがどのように私がそれを解釈するかです:
int *something[n];
優先順位に関する注意:配列添字演算子( '[]')は、間接参照演算子( '*')よりも優先順位が高くなります。
そのため、ここでは '*'の前に '[]'を適用して、ステートメントを次のようにします。
int *(something[i]);
int num
は(num)は(int)を意味し、int *ptr
またはint (*ptr)
は(value at ptr)は(int)を意味し、ptrをintへのポインタにします。
これは、((の何かのi番目のインデックスの値)の値)が整数であると読むことができます。つまり、(何かのi番目のインデックスの値)は(整数ポインタ)であり、これは何かを整数ポインタの配列にします。
2番目のものでは、
int (*something)[n];
この文から理解するには、この事実に精通している必要があります。
配列のポインタ表現についての注意:somethingElse [i]は*(somethingElse + i)と同等です。
そのため、somethingElseを(* something)に置き換えると、*(* something + i)が得られます。これは宣言に従って整数です。それで、(* something)は配列を与えて、それが(配列へのポインタ)と同等になるようにします。
私たちは単純な規則を使うことができると思います。
example int * (*ptr)()[];
start from ptr
"ptr
は"右に向く..its ")"へのポインタであり、今度はその "("外に出る "()"なので引数を取らない関数へ "左へ行く"そしてポインタを返す "配列の右へ "整数の"左へ "
私は2番目の宣言が多くの人にとって分かりにくいと思います。これを理解するのは簡単な方法です。
整数の配列、すなわちint B[8]
を持つことができます。
Bを指す変数Aもあります。今、Aの値はB、つまり(*A) == B
です。したがって、Aは整数の配列を指します。あなたの質問では、arrはAに似ています。
同様に、int* (*C) [8]
では、Cは整数へのポインタの配列へのポインタです。