ローカル変数へのアクセス
次のプログラムは、入れ子関数 getが、その外側の mainのローカル変数 vの値を参照する例である。
実行してみよう。
入れ子関数が、関数の中の単なるブロックであるかのように変数にアクセスできることがわかる。
#include
int main() {
int v = 5;
int get() { return v; }
printf("%d\n", get());
return 0;
}
実行してみよう。
$ make NestedFunctionExample11
cc NestedFunctionExample11.c -o NestedFunctionExample11
$ ./NestedFunctionExample11
5
入れ子関数が、関数の中の単なるブロックであるかのように変数にアクセスできることがわかる。
入れ子関数のポインタ
関数のポインタを渡して、その渡した先で vにアクセスできるだろうか。
subの中から呼び出されている関数 fが mainのローカル変数 vにアクセスできている。
#include
typedef int (*function)();
void sub(function f) {
printf("%d\n", f());
}
int main() {
int v = 5;
int get() { return v; }
sub(get);
return 0;
}
$ make NestedFunctionExample12
cc NestedFunctionExample12.c -o NestedFunctionExample12
$ ./NestedFunctionExample12
5
subの中から呼び出されている関数 fが mainのローカル変数 vにアクセスできている。
subは mainのスタックフレームにアクセスできるだけなのか、自身のフレームも持っているのだろうか。
これを見ると、自身のスタックフレームを持った上で、その外側にある関数のスタックフレームにもアクセスできるようだ。
#include
typedef int (*function)();
void sub(function f) {
printf("%d\n", f());
}
int main() {
int v = 5;
int get() {
int u = 3;
printf("%d\n", u);
return v + u;
}
sub(get);
return 0;
}
$ make NestedFunctionExample13
cc NestedFunctionExample13.c -o NestedFunctionExample13
$ ./NestedFunctionExample13
3
8
これを見ると、自身のスタックフレームを持った上で、その外側にある関数のスタックフレームにもアクセスできるようだ。
外側の関数から復帰後
入れ子関数を定義した関数から復帰してしまったらどうか。
多分外側の関数のスタックフレームはもう無いからアクセスできないはず。
あれ、うまくアクセスできてしまった。
実はこれ、GCC 4.6.0 on SunOS 5.11 (x86)環境ではうまく行ってしまう。(様に見える)
同じコードを GCC 4.6.3 on Ubuntu 12.04 (x86)でやってみると。
ちゃんと滅茶苦茶な値になった。(うまく行かなかったことを安心しているようで妙な表現だが)
#include
typedef int (*function)();
void sub(function f) {
printf("%d\n", f());
}
function getFunction() {
int v = 899;
int get() { return v; }
return get;
}
int main() {
function f = getFunction();
sub(f);
return 0;
}
多分外側の関数のスタックフレームはもう無いからアクセスできないはず。
$ make NestedFunctionExample14
cc NestedFunctionExample14.c -o NestedFunctionExample14
$ ./NestedFunctionExample14
3
902
あれ、うまくアクセスできてしまった。
実はこれ、GCC 4.6.0 on SunOS 5.11 (x86)環境ではうまく行ってしまう。(様に見える)
同じコードを GCC 4.6.3 on Ubuntu 12.04 (x86)でやってみると。
$ make NestedFuctionExample14
cc NestedFuctionExample14.c -o NestedFuctionExample14
$ ./NestedFuctionExample14
134513727
ちゃんと滅茶苦茶な値になった。(うまく行かなかったことを安心しているようで妙な表現だが)
Solarisでうまく行ったように見えたのはスタックフレームの内容が残っていたからで、いつもこう行くとは限らない。
たとえば、関数ポインタを取得してから呼び出すまでの間にスタックを使用するなにか別の関数を呼んでしまうと壊れてるはず。
この様に、printfを入れてみよう。
値が可笑しくなるだけでなく、アクセス違反が生じてしまった。
たとえば、関数ポインタを取得してから呼び出すまでの間にスタックを使用するなにか別の関数を呼んでしまうと壊れてるはず。
#include
typedef int (*function)();
void sub(function f) {
printf("%d\n", f());
}
function getFunction() {
int v = 899;
int get() {
int u = 3;
printf("%d\n", u);
return v + 3;
}
return get;
}
int main() {
function f = getFunction();
printf("Hello, world.\n");
sub(f);
return 0;
}
この様に、printfを入れてみよう。
$ make NestedFunctionExample15
cc NestedFunctionExample15.c -o NestedFunctionExample15
$ ./NestedFunctionExample15
Hello, world.
Segmentation Fault (core dumped)
値が可笑しくなるだけでなく、アクセス違反が生じてしまった。
0 件のコメント:
コメントを投稿