電子書籍の厳選無料作品が豊富!

C言語で正の整数n を受け取って、この数列の第1 項から第n 項までのフィボナッチ数列を求めて表示、および結果をファイルに保存するプログラムを作ってみました。
ですが、答えがおかしくなります(具体的にはーがつくものが交互にでてきます)・・・コンパイルはできたのですが・・・
環境はvisual c++2010expressです。
また、a[ ]の配列をもっと増やす方法はないでしょうか・・・大きな値を指定してやると、プログラムを実行したら「このプログラムは停止しました~」という画面が出てきます・・・
また、ファイルを保存するときにfprintfで保存しようと思うのですがa+だとうまく保存されるのですがw+だと最後から一つ前のものしか書き込まれません・・・なぜなのでしょうか・・
どなたか教えていただけないでしょうかm( )m
ソースはこちらです



#include<stdio.h>
int main(void){

int i;
int n;
int a[9999];

printf("n? ");
scanf("%d",&n);
while(n<0){
printf("nは0より大きい整数でお願いします\n");

printf("n? ");
scanf("%d",&n);
}




for(i=1;i<=n;i++){
if( i == 1 || i == 2 ){
a[i]=1;
}

else{
a[i]=a[i-1]+a[i-2];
}

}



for(i=1;i<=n;i++){
printf("a[%d] = %d\n",i,a[i]);
}



for (i=1;i<n;i++) {


FILE *file;
file = fopen("k04a.txt","w+");

fprintf(file,"a[%d] = %d\n",i,a[i]);
fclose(file);

}
return 0;


}

A 回答 (5件)

> a[46] = 1836311903


> a[47] = -1323752223

となることでしたら、符号付きint(32bit)で表現できる範囲をオーバーフローした結果なので「正しい」です。
a[45] = 1134903170 = 0x43a53f82
a[46] = 1836311903 = 0x6d73e55f
a[47] = 2971215073 = 0xb11924e1
↑最上位が1なので、符号付きの場合、負の値(2の補数表現)
→ -1323752223
また、符号無し(unsinged int)でやっても、
a[48] = 4807526976 = 0x11e8d0a40
→32bitまで
0x1e8d0a40 = 512559680
とオーバーフローします。

64bit整数を使うともう少し長くなりますが、93までです。

根本対策は、必要なだけのビット幅を持った整数を使うことです。
Cでも多倍長精度整数のライブラリがありますし、自作もでますが、
数列をファイルに出力するだけなら、PythonやRubyといった、無限精度整数を持っている言語を使うのが簡単です。

>また、a[ ]の配列をもっと増やす方法はないでしょうか・・・
>大きな値を指定してやると、プログラムを実行したら「このプログラムは停止しました~」という画面が出てきます・・・
9999 と指定しているので、9998までしか保証されません。
Cでは、配列の添字のチェックはしないので、それより大きな値を使ってもなにくわぬ顔で実行しようとします。

方法として
・nが決まってから、必要な大きさの領域をmalloc/callocを使って確保する(使用後の解放は忘れずに)
・今回ので言えば、直前2つの値だけわかればいいので、それだけを確保し、値は随時出力する。

>また、ファイルを保存するときにfprintfで保存しようと思うのですがa+だとうまく保存されるのですがw+だと最後から一つ前のものしか書き込まれません・・・なぜなのでしょうか・・

openしたときのファイルポインタの位置の違いでしょう。
w+はポインタが先頭なので、書き込むとそこから上書き
a+はファイルの最後に移動するので、書き込めばその続きから。

それより、1回のfopenでまとめて書いた方が効率よくないですか?

上の「2つだけ確保」と合せて

int a[2];
int A ;
a[0]=0;
a[1]=0;
FILE *file;
file = fopen("k04a.txt","w");
for (i=1;i<n;i++) {
if( i == 1 || i == 2 ){
A=1;
}else{
A=a[0]+a[1];
}
a[0]=a[1];
a[1]=A;

printf("a[%d] = %d\n",i,A);
fprintf(file,"a[%d] = %d\n",i,A);
}
fclose(file);

この回答への補足

>>となることでしたら、符号付きint(32bit)で表現できる範囲をオーバーフローした結果なので「正しい」です。

なるほどー
確かにそうでした!
ただunsigend charで指定しても47ですでに-1323752223だったり・・・・

>>根本対策は、必要なだけのビット幅を持った整数を使うことです。

おおお!多倍長精度整数のライブラリとかあるんですね!
ありがとうございます!
rudyやPythonは使ったことがなくて><
rudyはいつかやりたいんですけどね・・・・・

>>nが決まってから、必要な大きさの領域をmalloc/callocを使って確保する(使用後の解放は忘れずに)

mallocやcalloc関数ですかー実は初耳で(汗
勉強してきます!
>それより、1回のfopenでまとめて書いた方が効率よくないですか?
上の「2つだけ確保」と合せて

おおおおおお
なるほど!
確かにこういう風に書いたほうが効率いいですね!
いろいろと勉強になりました!
ありがとうございました!

補足日時:2010/08/19 23:13
    • good
    • 0

A No.1のKulesです。


まあ他の回答者が書かれているのですでにその間違いには気付かれているとは思いますが…
>そうかunsigned intなら整数ってことにできますねー^^
unsigned intにすることで、表現できる最大の整数の値がちょっと増える(大体倍になる)だけで、
本当に「焼け石に水」程度の効果しかありません。私も使ったことはありませんが、多倍長精度整数やら
無限精度整数やらといった、もっと大きな数値を扱えるような型を使うのがいいんでしょうね。

以上、参考までに。
    • good
    • 0
この回答へのお礼

わざわざありがとうございます!
たしかintでマイナスであった分がプラスで持ってこれるようになるから倍になるんでしたよね!
うろ覚えではありますが・・・・・・・

kmeeさんにも教えていただきましたが、多倍長精度整数やら無限精度整数等大きな数値を扱える型があるらしいですね!
自分も初めて聞きました・・・・・
この際なのでそれでもう一度やってみようかと思っています!
大変参考になりました!
ありがとうございます!

お礼日時:2010/08/19 23:28

intなので,nはせいぜい46までしか正しい値を示しません。


fopenやfcloseをforの外に出したほうがいい。
いずれも前の回答者さんのとおりです。
>一つ前のものしか書き込まれません
は,
for (i=1;i<n;i++) {
を,
for (i=1;i<=n;i++) {
にすればいいのではないですか。
    • good
    • 0
この回答へのお礼

あっ!
なんとイコールを付けていなかったとは
ありがとうございました!

お礼日時:2010/08/19 23:18

> a+だとうまく保存されるのですがw+だと最後から一つ前のものしか書き込まれません・・・



fopenのモードでは
・"a+" ・・・ 読み込み/追加書き出し
・"w+" ・・・ 読み込み/書き出し
となっています。

"a+" ではファイルの最後に追加していきます。
"w+" ではファイルの内容が失われます。

そもそも、for ループの中で fopenを行っているのはなぜでしょうか?

for (i=1;i<n;i++) {
fprintf(file,"a[%d] = %d\n",i,a[i]);
}

にして、
FILE *file;
file = fopen("k04a.txt","w+");
はプログラムの前の方に移動、

fclose(file);
は return 0;の直前に移動すれば良いのではないでしょうか。
    • good
    • 0
この回答へのお礼

>>そもそも、for ループの中で fopenを行っているのはなぜでしょうか?

確かにその必要はなかったです!
ありがとうございます!
おかげさまでファイルの書き込みはうまくいくようになりました!
まあ入力した項のひとつ前しかなぜか表示されませんが・・・

w+やa+の説明もわかりやすくありがとうございました!

お礼日時:2010/08/19 22:00

私もCはほとんど素人ですが…


フィボナッチ数列って結構な勢いで値増えていきますよね?第30項で83万ぐらいになってしまいます。
intってそんなに大きな数字は表せなかったような…
というわけで1つの解決法としては、ほぼ焼け石に水ですが
aをunsigned intで宣言する、あるいはlongとかlong longとかで宣言する
というのがあると思います。
また、aを9999までしか用意していないのなら、nを10000より大きな数字を入れた時点でバッファオーバーラン?とかいうのが起きてだめになります。

さらに言うと、Cでの配列は要素0から定義されていたはずなので、少々気持悪いですが
for(i=1;i<=n;i++){
ではなく
for(i=0;i<n;i++){
とした方がいいように思います。

以上、参考になれば幸いです。
    • good
    • 0
この回答へのお礼

>>intってそんなに大きな数字は表せなかったような

あっ・・・・・・・・そうでしたそういえば制限がありました・・・・・・
なるほど!ありがとうございました!

>>さらに言うと、Cでの配列は要素0から定義されていたはずなので、少々気持悪いですが
for(i=1;i<=n;i++){
ではなく
for(i=0;i<n;i++){
とした方がいいように思います。


確かに0から指定してやることができるのはできるのですが、ここでは表示するときにa[1]から表示して見たかったので・・・・


ありがとうございました!助かりました!
そうかunsigned intなら整数ってことにできますねー^^

お礼日時:2010/08/19 22:14

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!