回答:
int* arr[8]; // An array of int pointers.
int (*arr)[8]; // A pointer to an array of integers
3つ目は1つ目と同じです。
一般的なルールは、演算子の優先順位です。関数ポインタが画面に入ると、さらに複雑になる可能性があります。
( ) [ ]
左から右に関連付けられ、各要素がintを指すサイズ8の配列として、および整数を保持するサイズ8の配列へのポインターとして*
読み取らint* arr[8]
れるよりも優先順位が高いint (*arr)[8]
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
[5]
)は内側の次元を表します。これは、これ(*a[8])
が最初の次元であり、配列の外部表現であることを意味します。何内の各要素a
を指すサイズ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つの整数の配列へのポインターとしてボーダーを宣言します。ただし、そのポインタは、他のそのような配列をいつでもポイントできます。代わりに、この種の引数の動作が必要です(この関数はこれらの整数を変更しないため)。使用後の宣言。
(ps:このサンプルを自由に改善してください!)
typedef int (*PointerToIntArray)[];
typedef int *ArrayOfIntPointers[];
経験則として、(のような右単項演算子[]
、()
などは、)左のものよりも優先してください。したがって、int *(*ptr)()[];
intへのポインタの配列を返す関数を指すポインタになります(かっこから抜けたらできるだけ早く正しい演算子を取得してください)
error: ‘foo’ declared as function returning an array int foo(int arr_2[5][5])[5];
GCC 8の下で$ gcc -std=c11 -pedantic-errors test.c
int *(*ptr)();
はp()[3]
(または(*p)()[3]
)のような式を後で使用できます。
int *foo(int arr_2[5][5]) { return &(arr_2[2][0]); }
そして、このようにそれを呼び出す:foo(arr)[4];
含めるべきarr[2][4]
、右?
簡単なルールが使えると思います。
example int * (*ptr)()[];
start from ptr
" ptr
は、"右方向に行く..its ")"へのポインターです。今、左方向に移動します。 「配列へ」右「整数の左へ」
)
は今すぐ左に行く...それは*
「へのポインタ」です右に行く...それは)
、今左に行く...それは(
出てくる、右に行く()
...右に行く「引数を取らない関数に」そう[]
「との配列を返す」右に行く;
の端を、その左に行く... *
行く左...「へのポインタ」int
「整数」
Cで複雑な型を読み取る方法を説明する興味深いWebサイトは次のとおりです。http: //www.unixwiz.net/techtips/reading-cdecl.html
ここに私がそれを解釈する方法があります:
int *something[n];
優先順位に関する注意:配列添字演算子(
[]
)は、逆参照演算子(*
)よりも優先されます。
したがって、ここでは[]
before を適用*
し、ステートメントを次と同等にします。
int *(something[i]);
宣言がどのように意味をなすかに注意してください。
int num
平均num
はint
、int *ptr
またはint (*ptr)
平均、(値atptr
)はでありint
、これはへptr
のポインターになりint
ます。
これは、((何かのi番目のインデックスの値)の値)は整数として読み取ることができます。したがって、(何かのi番目のインデックスの値)は(整数ポインター)であり、これにより、何かが整数ポインターの配列になります。
二つ目は
int (*something)[n];
このステートメントを理解するには、この事実に精通している必要があります。
配列のポインタ表現に関する注意:
somethingElse[i]
と同等*(somethingElse + i)
したがって、に置き換えるsomethingElse
と(*something)
、*(*something + i)
宣言に従って整数であるが得られます。したがって、(*something)
配列を指定すると、(配列へのポインター)と同等のものになります。
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)。