2013/12/07

C言語Tips1 ポインタの問題!

C言語のポインタで躓く人が多いそうです。今回は問題を出します!

問題 : 「void (*(*p)[3])(void* (*[])(void));」をmain関数の中で宣言しました。

int main(void) {
    void (*(*p)[3])(void* (*[])(void));
    return 0;
}

このpを使ってサンプルプログラムを作りなさい。回答例は続きを読むで!

(回答例)

#include <stdio.h>

void f(void* (*p[])(void));
void* g(void);
void h(void);

void f(void* (*p[])(void)) {
    printf("in f()\n");
    ((void (*)(void))((*p[0])()))();
}

void* g(void) {
    printf("in g()\n");
    return h;
}

void h(void) {
    printf("in h()\n");
}

int main() {
    void (*(*p)[3])(void* (*[])(void));
    void (*a[3])(void* (*[])(void));
    void* (*b[3])(void);

    p = &a;
    (*p)[0] = f;
    b[0] = g;
    (*a[0])(b);
}

(解説)
まず
void (*(*p)[3])(void* (*[])(void));
を解析しましょう。
 1. (*p)だからこれは変数pです。この変数は*が付いているのでポインタです。
2. (*p)[3]だからこれは配列です。そしてこの配列のポインタがpです。
3.  (*(*p)[3])(void* (*[])(void))だからこれは関数ポインタです。そして、その関数ポインタの配列のポインタがpです。
 つまりこれは、関数ポインタの配列へのポインタを表しています。

pはポインタですから、実体が必要ですね。実体を定義しましょう。
実体は関数ポインタの配列ですね。
void (*a[3])(void* (*[])(void));
としました。これは関数ポインタ(*)(void* (*[])(void))の要素3の配列です。
それではこの実体をpに入れましょう。pは配列へのポインタですから、定義した配列aのポインタを入れます。
 p = &a;
ここでp = &a[0];とするとコンパイルエラーとなります。 なぜならば、&aは配列へのポインタを表しますが、&a[0]では要素へのポインタとなるからです。
さて、pは実体を指していますから、これを使ってaを変更してみましょう。
(*p)[0] = f;
まずpはポインタですから、実体を表すために(*p)とします。これで配列自体をら和しますね。
(*p)[0]で0番目の要素を表します。
(*p)[0] = f;
で関数ポインタを代入します。
もちろんfとは、 void f(void* (*[])(void));です。

次にfの引数を入れるために「void* (*[])(void)」の部分を解析します。
1. これは引数の「void* (*name[])(void)」のnameが省略されていると判断します。
2. name[]なのでこれは配列です。
3. void* (*name[])(void)はvoid*型を戻り値とした関数ポインタの配列ですね。

fを実行するために引数である配列を定義します。
ただの配列ではなく、関数ポインタの配列ですね。
void* (*b[3])(void);
としました。

このbの0番目に関数の実体を入れます。
b[0] = g;

もちろんgとはvoid* g(void);です。

最後に実行してみましょう。関数ポインタの配列aの0番目の実体の関数(*a[0])に対して引数である配列bを入れるので、
(*a[0])(b);
とします。

fの引数は関数ポインタの配列なので、その0番目を呼び出します。p[0]
そしてこれの実体は*p[0]ですから、これに引数を与えて(今回はなし)実行します。
(*p[0])()ですね。
そして、この戻り値はvoid*型で、関数ポインタと想定します。
関数はvoid h(void)としましょう。この関数ポインタにキャストします。
(void (*)(void))((*p[0])())
これに引数(今回はなし)を与えて実行します。
((void (*)(void))((*p[0])()))();
ですね。

(まとめ)
今回は分かりづらい、
1. ポインタの配列
2. 配列のポインタ
3. 関数ポインタ
4. void*型
をmixしてみたものです。実用的でないサンプルですが、このくらい解析できるようにして貰いたいものです。

0 件のコメント:

コメントを投稿