【配列とポインタ】第4回 式の展開規則(配列・ポインタ編)

式の展開規則

実際のコーディングでは、扱う配列はたかだか二次元配列までがほとんどなので、一次元・二次元配列の展開手順は反復練習で身体で覚えてしまって、それ以上の多次元配列になれば、以下の展開規則を再帰的に当てはめて展開していけばいいかと思います。ポインタと配列の解読は、初めのうちはメモリの図を書いて幾何的に解くようにし、頭の中だけで絵が描けるぐらいメモリイメージが強固になれば、解析的にも解くようにすると、読解がより正確なものになると思います。

  1. 式中のオブジェクトαは、αを戻り値とする。
  2. 戻り値αが配列型以外の場合、αを戻り値とする。
  3. 戻り値αが配列型の場合、αの先頭要素へのポインタを戻り値とする。
  4. 戻り値αがポインタ型の場合、αの直前に「*」または直後に「[0]」をつけると、αの指すオブジェクトを戻り値とする。
  5. 戻り値αに対して、以上の規則を再帰的に適用した結果α'を戻り値とする。
  6. 以上の規則を適用した結果の戻り値のみを、戻り値とする。

ちょっと面白いのは規則4で、int型へのポインタ「int *pi」は、「*pi」だけでなく、「pi[0]」でも間接参照できるんですね。piの参照先が配列型オブジェクトじゃないのに、です。やはり、添字は間接演算子シンタックスシュガーにすぎない。

以上の展開規則を、以下実例で確認してみます。

配列の展開例

char arr[] = "abc";
*arr;

    +---+---+---+---+
arr | a | b | c |\0|
    +---+---+---+---+
  1. 式中の「arr」は、「arr」を戻り値とする(規則1)。
  2. 戻り値「arr」は配列型なので、先頭要素'a'へのポインタを戻り値とする(規則3)。
  3. 戻り値「'a'へのポインタ」はポインタ型なので、「*a」または「a[0]」は、「'a'」を戻り値とする(規則4)。
  4. 以上より、式「*arr」は「'a'」を戻り値とする。

配列型配列の展開例

char arr[][4] = {"abc", "def", "ghi"};
**arr;

    +---------------+---------------+---------------+
arr | a | b | c |\0| d | e | f |\0| g | h | i |\0|
    +---------------+---------------+---------------+
  1. 式中の「arr」は、「arr」を戻り値とする(規則1)。
  2. 戻り値「arr」は配列型なので、先頭要素"abc\0"へのポインタを戻り値とする(規則3)。
  3. 戻り値「"abc\0"へのポインタ」はポインタ型なので、「*arr」または「arr[0]」は配列「"abc\0"」を戻り値とする(規則4)。
  4. 戻り値「"abc\0"」は配列型なので、先頭要素「'a'」へのポインタを戻り値とする(規則3)。
  5. 以上より、式「*arr」は「'a'」へのポインタを戻り値とする。
  6. 戻り値「'a'へのポインタ」はポインタ型なので、「*(*arr)」または「(arr[0])[0]」は、「'a'」を戻り値とする(規則4)。
  7. 以上より、式「**arr」は「'a'」を戻り値とする。

配列へのポインタ変数の展開例

char arr[][4] = {"abc", "def", "ghi"};
char (*p)[];    /* 配列へのポインタ変数 */

p = arr;
**p;

    +---------------+---------------+---------------+
arr | a | b | c |\0| d | e | f |\0| g | h | i |\0|
    +---------------+---------------+---------------+
  1. 式中の「arr」は、「arr」を戻り値とする(規則1)。
  2. 戻り値「arr」は配列型なので、先頭要素"abc\0"へのポインタを戻り値とする(規則3)。
  3. pには、配列"abc\0"へのポインタが代入される。
  4. 式中の「p」は、「p」を戻り値とする(規則1)。
  5. 戻り値「p」はポインタ型なので、配列"abc\0"へのポインタを戻り値とする(規則4)。
  6. 戻り値「"abc\0"へのポインタ」はポインタ型なので、「*p」は配列「"abc\0"」を戻り値とする(規則4)。
  7. 戻り値「"abc\0"」は配列型なので、先頭要素「'a'」へのポインタを戻り値とする。
  8. 以上より、式「*p」は「'a'」へのポインタを戻り値とする。
  9. 戻り値「'a'へのポインタ」はポインタ型なので、「*(*p)」は「'a'」を戻り値とする。
  10. 以上より、式「**p」は「'a'」を戻り値とする。