こんにちはゲストさん。会員登録(無料)して質問・回答してみよう!

解決済みの質問

プログラムがわかりません

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の最初の引数は出力先ですが、なぜ標準エラー出力なの   でしょうか?

時間のあるかた解説お願いします。

投稿日時 - 2008-12-12 23:49:18

QNo.4551085

困ってます

質問者が選んだベストアンサー

なにを元にスタックオーバーフローといっているのか
分かりませんが

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の最初の引数は出力先ですが、なぜ標準エラー出力なの   でしょうか?
てっとり早いからではないでしょうか?


アドバイスとして市販されている参考書を元に
勉強された方がよいかと思われます

投稿日時 - 2008-12-13 09:19:03

お礼

回答ありがとうございます。皆様の回答でだいぶ納得がいったのですが、最後にわからないのが、buf[i] = hello; のループです。
これは関数helloの先頭のアドレスを配列buf[i]に格納していると解釈しています。でもそれなら、
int a;
int *p;
p = &a;

のように関数に&がいるのではと思うのですが。

投稿日時 - 2008-12-13 18:08:20

ANo.3

このQ&Aは役に立ちましたか?

0人が「このQ&Aが役に立った」と投票しています

回答(4)

ANo.4

Wr5

>のように関数に&がいるのではと思うのですが。

関数名のみ書かれた場合は、その関数へのポインタとなります。
規格書に書かれているのではないかと思われますが、私自身は規格書をみたことはありませんので…。

「関数ポインタ」あたりで検索すると見つかるかと思われます。
汎用ポインタに関数のアドレスを入れる。という方法が今回のものかと。



ちなみに、スタックオーバーフローは最初に用意されているスタック領域を使い切ってしまった場合のことを言います。
再帰呼び出しを繰り返していたり、サイズの大きめなローカル変数を何度も取っていたり等。
処理系によってデフォルトのスタックサイズは異なります。
数k程度のスタック領域しかないものから1Mのスタック領域を持つもの等……。

投稿日時 - 2008-12-14 00:36:19

お礼

回答ありがとうございます。大変たすかりました。

投稿日時 - 2008-12-14 07:43:11

3. void *buf[10];
voidポインタ型の配列を宣言しています。

4. buf[i] = hello; のループ
   これもわかりません。配列に関数を代入しているのでしょうか?
そうです。
   
5.  fprintf(stderr,"hello!\n"); これもまたわかりません。
stderrはstdio.hでFILE*型で定義されています。ここに書き込むと画面に出力されます。
main()のbuf[100]は意味がよく分かりません。

投稿日時 - 2008-12-13 02:03:45

お礼

回答ありがとうございます、助かりました。

投稿日時 - 2008-12-13 16:21:23

ANo.1

Wr5

スックオーバーフローではなく、バッファオーバーランではないですか?
# もっともも掲示のコードではオーバーランしないハズですけど。

>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の最初の引数は出力先ですが、なぜ標準エラー出力なの   でしょうか?

戻りアドレスを書き換えて実行された。ということを確認するためでしょう。
標準エラー出力ならばバッファリングされないでしょうから、プログラムが不正終了しても画面に出力される。
ということを期待したためかと思われます。

投稿日時 - 2008-12-13 01:58:15

お礼

回答ありがとうございます。ループは(i=0;i<100;i++)でした。

投稿日時 - 2008-12-13 16:20:39

あなたにオススメの質問