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 件のコメント:
コメントを投稿