Cの変数の構文は式の構文に似せてある。これはこれで合理的なんだけど、どういう変数なのかイメージがつかみにくい。

しかし逆に考えると、宣言文をればこの変数がどうつかわれるか書いてあるわけで、そういう目でみればすごくわかりやすい。

でも関数適用や配列が後置演算子なのにポインタdereferenceが前置演算子がなのがよくなかった。

こいつのせいでポインタ変数があると左から右へと一方向に読めない。

よみかた基本戦略

  • まず宣言している識別子(変数名とか関数名とか)をさがす。型名をはずしていけば1個所残るのでわかる。
  • 演算子の優先順序にしたがって変数に意味をつけていく。
  • constやvolatileは形容詞だとおもって読む。

以下実例:

int a;

a       変数aの宣言だな...
int a   aの型はintだ。

int a[10];

    a      変数aの宣言だな..
    a[10]  aは大きさ10の配列だな.. その要素は...
int a[10]  要素の型はintだ。 

int a[10][3];

    a         変数aの宣言だな..
    a[10]     aは大きさ10の配列だな.. その要素は...
    a[10][3]  要素は大きさ3の配列だな.. その要素は...
int a[10][3]  要素の型はintだ。

int *a[10];

     a        変数aの宣言だな..
     a[10]    aは大きさ10の配列だな.. その要素は...
    *a[10]    要素はポインタだな... dereferenceすると..
int *a[10]    dereferenceした型はintだ。

double (*a)[3];

         a      変数aの宣言だな..
        *a      aはポインタだな... dereferenceすると...
       (*a)[3]  dereferenceした型は大きさ3の配列だな... その要素は...
double (*a)[3]  要素の型はintだ。

int func(int a);

    func         変数funcの宣言だな..
    func()       funcは関数宣言だった...
    func(int a)  パラメータは1つだな...
int func(int a)  関数を呼んだときの戻り値の型はintだ。

int (*func)(int a);

      func          変数funcの宣言だな..
     *func          funcはポインタだな... dereferenceすると...
    (*func)()       dereferenceすると関数だな...
    (*func)(int a)  パラメータは1つだな...
int (*func)(int a)  関数を呼んだときの戻り値はintだ。

Cの変数宣言を読む上で、識別子が宣言したい名前なのかすでに定義されている型名なのかが判断できないといけないのだが、

命名規則に頼るしかないところが、Cの宣言文法の問題だろう。

コンパイラはとうぜんヘッダファイルを全部読むので問題ないが、人間は命名規則と常識をベースに判断するしかないのでつらい。

まぁ、実際C++では変数定義なのか関数宣言なのか構文上区別がつかないことがあるくらいむづい。

typedef int T;
int
foo()
{
    T t (); // 変数tの定義で型はTでデフォルトコンストラクタで初期化、のつもりが
            // 関数tの宣言で戻り値の型がTでパラメータなし、と解釈されてしまう...
    // clang34 says:
        // warning: empty parentheses interpreted as a function declaration
        // note: replace parentheses with an initializer to declare a variable

    return t;
    // gcc49 says:
        // error: invalid conversion from 'T (*)() {aka int (*)()}' to 'int
    // clang34 says:
        // error: cannot initialize return object of type 'int' with an lvalue of type 'T ()'
}
int
bar()
{
    T t {};   // 変数tの型はTで、初期化子は空っぽ。
    return t;
}

koie