- ベストアンサー
プログラムがわかりません
C言語の本を読んでいるんですが、詰まってしまいました。プログラム自体は単純なのですが #include<stdio.h> void hello(void) { fprintf(stderr,"hello!\n"); } void func(void) { void *buf[10]; static int i; for(i=0;i<10;i++) { buf[i] = hello; } } int main(void) { int buf[100]; func(); return 0; } のスタックオーバーフローのプログラムです。 1. 要素100のint型配列を宣言 2. 関数funcの呼び出し 3. void *buf[10]; まずここでがわかりません。なぜポインタが でてきたのか?またbufの要素数は100では? 4. buf[i] = hello; のループ これもわかりません。配列に関数を代入しているのでしょうか? 5. fprintf(stderr,"hello!\n"); これもまたわかりません。 fprintfの最初の引数は出力先ですが、なぜ標準エラー出力なの でしょうか? 時間のあるかた解説お願いします。
- みんなの回答 (4)
- 専門家の回答
質問者が選んだベストアンサー
なにを元にスタックオーバーフローといっているのか 分かりませんが 1. 要素100のint型配列を宣言 main関数内 の int buf[100] にて行っていますが使用されていません 2. 関数funcの呼び出し func() のところで関数func()が呼び出されます 3. void *buf[10]; まずここでがわかりません。なぜポインタが でてきたのか?またbufの要素数は100では? 要素数100はmain関数のbufです ここではfunc関数内なので別のbuf変数となります 4. buf[i] = hello; のループ これもわかりません。配列に関数を代入しているのでしょうか? そのようですが多少間違っています void (*buf[10])(); と宣言しましょう 5. fprintf(stderr,"hello!\n"); これもまたわかりません。 fprintfの最初の引数は出力先ですが、なぜ標準エラー出力なの でしょうか? てっとり早いからではないでしょうか? アドバイスとして市販されている参考書を元に 勉強された方がよいかと思われます
その他の回答 (3)
- Wr5
- ベストアンサー率53% (2173/4061)
>のように関数に&がいるのではと思うのですが。 関数名のみ書かれた場合は、その関数へのポインタとなります。 規格書に書かれているのではないかと思われますが、私自身は規格書をみたことはありませんので…。 「関数ポインタ」あたりで検索すると見つかるかと思われます。 汎用ポインタに関数のアドレスを入れる。という方法が今回のものかと。 ちなみに、スタックオーバーフローは最初に用意されているスタック領域を使い切ってしまった場合のことを言います。 再帰呼び出しを繰り返していたり、サイズの大きめなローカル変数を何度も取っていたり等。 処理系によってデフォルトのスタックサイズは異なります。 数k程度のスタック領域しかないものから1Mのスタック領域を持つもの等……。
お礼
回答ありがとうございます。大変たすかりました。
3. void *buf[10]; voidポインタ型の配列を宣言しています。 4. buf[i] = hello; のループ これもわかりません。配列に関数を代入しているのでしょうか? そうです。 5. fprintf(stderr,"hello!\n"); これもまたわかりません。 stderrはstdio.hでFILE*型で定義されています。ここに書き込むと画面に出力されます。 main()のbuf[100]は意味がよく分かりません。
お礼
回答ありがとうございます、助かりました。
- Wr5
- ベストアンサー率53% (2173/4061)
スックオーバーフローではなく、バッファオーバーランではないですか? # もっともも掲示のコードではオーバーランしないハズですけど。 >1. 要素100のint型配列を宣言 関数func()でバッファオーバーランした際にmain()の戻り先アドレスを破壊させないために確保されているのでしょう。 >2. 関数funcの呼び出し この関数内でバッファオバーランを起こす予定だったのかと。 >3. void *buf[10]; まずここでがわかりません。なぜポインタが でてきたのか?またbufの要素数は100では? >4. buf[i] = hello; のループ > これもわかりません。配列に関数を代入しているのでしょうか? func()の戻り先のアドレスをhello()の開始アドレスに書き換えを行いたかったものと推測されます。 その際、「hello()の開始アドレス」を書き込むためにポインタが必要だったのでしょう。 あと、「またbufの要素数は100では?」はmain()のbufですよね? main()のbufsfunc()のbufは別のモノになります。 それぞれの関数内の『ローカル変数』ですから。 そして、【ローカル変数用に確保された領域】を超えて書き込んで戻りアドレスを書き換える。 としたかったようですが、forループが「正しい」回数になっているため領域を超えていません。 # ついでに、戻りアドレスがfunc()内のローカル変数bufより前にあるか後にあるかは処理系依存です。 # そもそもその場所に戻りアドレスが置かれているかどうかさえも処理系依存だったかと。 # たいていは戻りアドレスやローカル変数はスタック上に積まれますが。 # ループカウンタのiがstatic変数なのはスタック上に置かれないようにするため…かと。 >for(i=0;i<10;i++) を for(i=-1;i<=10;i++) とすればローカル変数のバッファオーバーランになります。 >5. fprintf(stderr,"hello!\n"); これもまたわかりません。 > fprintfの最初の引数は出力先ですが、なぜ標準エラー出力なの でしょうか? 戻りアドレスを書き換えて実行された。ということを確認するためでしょう。 標準エラー出力ならばバッファリングされないでしょうから、プログラムが不正終了しても画面に出力される。 ということを期待したためかと思われます。
お礼
回答ありがとうございます。ループは(i=0;i<100;i++)でした。
お礼
回答ありがとうございます。皆様の回答でだいぶ納得がいったのですが、最後にわからないのが、buf[i] = hello; のループです。 これは関数helloの先頭のアドレスを配列buf[i]に格納していると解釈しています。でもそれなら、 int a; int *p; p = &a; のように関数に&がいるのではと思うのですが。