2018年8月29日水曜日

C言語 ポインタ宣言の読み方

2016 Dec. 25.

http://kmaebashi.com/programmer/pointer.html 参照方。
 
( http://news.mynavi.jp/articles/2008/04/18/pointer/002.html より)
char *ap[3];
  あるapという名前のものがある
    ↓
    apは配列の先頭アドレスを表す定数で、配列要素ap[x]を求めることができる
    ↓
    ap[x]はポインタであり、その指し示す先の「*ap[x]」はchar型である
    以上により、「char *ap[3];」が「ポインタの配列」を宣言している

char **pp;     ← 「char型へのポインタ」へのポインタppを宣言
  char *ap[3];   ← 「char型へのポインタ」を要素とする配列ap(要素数3)を宣言
  char a[5];     ←  char型を要素とする、要素数5の配列aを宣言

ap[2] = a;                    ← ap[2](ポインタ)にaのアドレス定数を代入
a[3] = 'Y';                   ← a[3]には文字'Y'を代入
pp = ap;                      ← apのアドレス定数はポインタのポインタに代入可
printf("%c\n", ap[2][3]);     ← apに[ ]の添字を2つ付けて内容を読む
printf("%c\n", pp[2][3]);     ← ppを使っても同様に記述できる
printf("%c\n", *(*(ap+2)+3)); ← ポインタとして記述する場合はこうなる
printf("%c\n", *(*(pp+2)+3)); ← ppを使う場合も同様
printf("%c\n", *(ap[2]+3));   ← 添字の[ ]とポインタの*が混在した記述も可能
printf("%c\n", *(pp[2]+3));   ← ppを使う場合も同様
printf("%c\n", (*(ap+2))[3]); ← 添字の[ ]とポインタの*を入れ替えた場合
printf("%c\n", (*(pp+2))[3]); ← もちろんppを使っても同様
 
( http://news.mynavi.jp/articles/2008/04/18/pointer/003.html より)
「配列へのポインタ」の宣言の「char (*pa)[5];」は、次のように考えます。

     あるpaという名前のものがある
     ↓
     paはポインタであり、paが指し示す先(*pa)は、何らかの要素数5の配列全体である
     ↓
    その配列の各要素「(*pa)[x]」はchar型である
    「配列へのポインタ」は、単に配列の先頭アドレスを指したポインタではありません。このポインタ(pa)は所定の大きさの配列全体を指しており、paをインクリメントすると、paが指し示す配列のサイズ分(5バイト)、アドレス値が加算されます。このため、pa自体はポインタであっても、宣言時にはそのポインタの指し示す先の配列の大きさを指定する必要があります。
    なお、paの宣言では、[5]という値を指定していますが、実際の配列用のメモリは(宣言しただけでは)確保されません。

( http://blog.tojiru.net/article/426537111.html より )
日本語らしくしたい場合は、最後に順番をひっくり返します。

(1) 識別子を探す。

(2) 識別子の両隣から読み始めるが、次の順番で結合される。
  1. 優先度を表す()
  2. 関数を表す()、配列を表す[]
  3. ポインタを表す*
(3) 配列はarray(要素数) of ...
      関数はfunction(引数) returning ...
      ポインタはpointer to ...
      という風に英語で置き換えてゆく。

(4) 最後に派生元の型(intとかfloatとか)を付けて終わり。

  *a[10]*a(void)みたいに両側に何か書いてある時は、配列や関数の方が勝つ。

 int (*(*a(void))[10])(void);
   a is function(void) returning pointer to array(10) of pointer to function(void) returning int.
   aは、int を返す関数(void) へのポインタ の配列(要素数10) へのポインタ を返す関数(void)
 
 void (*signal(int, void (*)(int)))(int);
   signal is function(int, pointer to function(int) returning void) returning pointer to function(int) returning void.
   signalは、void を返す関数(int) へのポインタ を返す関数(int, voidを返す関数(int) へのポインタ) 


( https://naofumi.castle104.com/c%E8%A8%80%E8%AA%9E%E3%81%AE%E3%83%9D%E3%82%A4%E3%83%B3%E3%82%BF%E5%AE%A3%E8%A8%80%E3%81%AE%E8%AA%AD%E3%81%BF%E6%96%B9/ より ) 
  1. 最初は変数名から出発して、型まで
    char *(*(**foo [][8])())[];
    foo ischar
  2. 変数名の右の[]
    char *(*(**foo [][8])())[];
    foo is array of … char
  3. さらに右に行って[8]
    char *(*(**foo [][8])())[];
    foo is array of array of 8 … char
  4. )にぶつかったので、変数名の左のポインタ演算子*を処理
    char *(*(**foo [][8])())[];
    foo is array of array of 8 pointer to … char
  5. さらにその左のポインタ演算子*
    char *(*(**foo [][8])())[];
    foo is array of array of 8 pointer to pointer to … char
  6. ()の中の処理が終わったので、右に移動。そこには関数を表す()があります
    char *(*(**foo [][8])())[];
    foo is array of array of 8 pointer to pointer to function returning … char
  7. ここで)にぶつかるので、左に戻ってポインタ演算子*を処理
    char *(*(**foo [][8])())[];
    foo is array of array of 8 pointer to pointer to function returning pointer to … char
  8. 左に行くと(にぶつかるので、この()は処理が終了。次に右に行って[]を処理
    char *(*(**foo [][8])())[];
    foo is array of array of 8 pointer to pointer to function returning pointer to array of … char
  9. これで右端まで行ったので、左に戻って一番左のポインタ演算子*を処理
    char *(*(**foo [][8])())[];
    foo is array of array of 8 pointer to pointer to function returning pointer to array of pointer to char


0 件のコメント:

コメントを投稿