アプリ版:「スタンプのみでお礼する」機能のリリースについて

次のプログラムは入力された行を読み込み、コマンドラインで指定されたファイルに書き込みます。
空白行が入力されたら、入力の終了とみなしてファイルを閉じます。続いてファイルを入力用に開き、
fgets()を使ってファイルの内容を表示するものです。

(ソースコードが長くてすみません)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char *argv[])
{
FILE *fp;
char str[80];

/* コマンドライン引数を検査する */
if(argc!=2) {
printf("ファイル名を指定してください\n");
exit(1);
}

/* 出力用にファイルを開く */
if((fp = fopen(argv[1], "w"))==NULL) {
printf("ファイルを開くことができません\n");
exit(1);
}

printf("終了するには空白行を入力してください\n");
do {
printf(": ");
gets(str);
strcat(str, "\n");/* 改行を追加する */
if(*str != '\n') fputs(str, fp);
} while(*str != '\n');
fclose(fp);

/* 入力用にファイルを開く */
if((fp = fopen(argv[1], "r"))==NULL) {
printf("ファイルを開くことができません\n");
exit(1);
}

/* ファイルを読み込み直す */
do {
fgets(str, 79, fp);
if(!feof(fp)) printf(str);
} while(!feof(fp));
fclose(fp);

return 0;
}

【質問】fgets()内のint型の数値「79」がどうして79なのかが分かりません。
    80でも良いような気がするのですが・・・

A 回答 (5件)

char str[80];


で文字列用の配列を80バイト分確保していますが文字列の終端には'\0'が必要です。
そのため実際に使える文字列の長さは79になります。
それを意識して
fgets(str, 79, fp);
としたのだと思いますがfgetsの引数は'\0'を含んだ数なので
fgets(str, 80, fp);
で問題ないでしょう。
    • good
    • 0
この回答へのお礼

さっそくの回答ありがとうございます!
fgets(str, 80, fp);
にしても問題なく実行できたのでおかしいな?と思って質問しました。

私が学習している本に、
『char *fgets(char *文字列, int 数値, FILE *ストリーム);

 fgets()関数は、「ストリーム」に結び付けられているファイルから文字を読み込み、
 「文字列」が指す文字列に格納していきます。この操作は「数値」マイナス1個分
 の文字を読み込むか、改行文字に出会うか、あるいはファイルの終わりに達するまで
 続けられます。いずれの場合も、格納された文字列の後にヌル文字が追加されます。
 類似のgets()と違って、改行文字も格納されます。』
とあります。

また質問で恐縮なんですが、「数値」は何のためにあるのでしょうか?
あと、改行文字が格納されることと数値が79なのは無関係ですか?

未熟者の私ですが、どうか教えてください。お願いします。

お礼日時:2007/12/11 15:43

>ご指摘のとおりに実行してみましたが、エラーが出ません。



strの中に「%s」って文字列が入っていた時に
printf(str);
を実行するのは
printf("%s");
を実行するのと同じです。

printf("%s");
って書いた時に「何かが足りないような?」って思いませんか?

では、strの中に「%3.3d」って文字列が入ってる時は?
printf(str);
を実行するのは
printf("%3.3d");
を実行するのと同じです。

こっちも「何かが足りない」って感じませんか?

そうです。「書式文字列に、2個目以降の引数の型を指定してるのに、2個目以降の引数が無い」ですね。

printfは「ある筈の2個目以降の引数が無いと、不定な値が渡され、何が起きるか判らない」のです。

>正:aaa(null)\naaaa\n

エラーは起きてます。

起きてますが「引数が足りずに不定な値が渡されたが、偶然に0が渡され、ヌルポインタとして判断されて『(null)』が代わりに表示され、致命的なエラーを免れた」だけです。

実はVisual StudioのCコンパイラのライブラリは「printfの書式文字列の変換ルーチンで、ポインタ(文字列のアドレス)を受け取った時、ポインタがヌルだったら『(null)』と表示し、致命的例外を出さないよう、対策が施してある」のです。

今回は「偶然に」ヌル対策で助かりましたが、いつもそうとは限りません。次の実行時、メモリの中にゼロじゃない何かの残骸が残ってたら「ヌルにならず、例外処理されて、赤バッテンのダイアログが出て強制終了」になるかも知れません。

>それでもやっぱり、printf("%s", str); としたほうが良いですか?
もちろん。strの中身に何が来るか判らないので。

それに「printfの第1引数は、出力したい文字列ではなく、書式文字列を指定する」ので「文字列を出力したいなら、書式文字列に%sを指定し、引き続く引数に文字列を指定する」のが「本当の使い方」です。

それとANo.3の方が「わざわざ効率の悪いprintf」と言っていますが、書式文字列を元に最終的な出力文字列に変換する際の処理や、前述のような「ヌルポインタで例外を出さない対策」などが「効率が悪い」原因になっています。

最適な解はANo.3、4のように「fputs(str,stdout)」を使う事です。
    • good
    • 0
この回答へのお礼

質問に答えてくださってありがとうございます!!
大変勉強になりました!
これからはprintf(str); のような書き方に気をつけていこうと思います。
丁寧に回答してくださってありがとうございました。

お礼日時:2007/12/12 11:41

> fputs()関数はファイルに書き込むための関数だから、表示できないのでは?



ファイルというよりはストリームに書き込むための関数ですね。
printfもストリームに書き込むための関数です。そして、printfを使ったとしても、「表示」できるかどうかは環境に依存します。
ちなみに、printfの出力先はstdoutです。ですから、fputsでstdoutを指定すれば、printfと出力先が同じになります。
    • good
    • 0
この回答へのお礼

回答ありがとうございます!
fputs(str, fp);
に勝手に変えてました。ごめんなさい!
>ちなみに、printfの出力先はstdoutです。ですから…
この文章を読んでからfputs(str, stdout)にしたら表示できました!
大変勉強になりました。ありがとうございました。

お礼日時:2007/12/11 17:26

> printf("%s", str); としたほうが良いですか?



わざわざ効率の悪いprintfを使わなくても、fputs(str, stdout); とすればよいのでは? 書式文字列に絡むバグも fputs なら起こりようがないですし。
    • good
    • 0
この回答へのお礼

回答ありがとうございます!
fputs(str, stdout); ですか・・・
推測でしかないんですけど、
fputs()関数はファイルに書き込むための関数だから、表示できないのでは?

お礼日時:2007/12/11 16:10

fgetsは「自動的に付けられる文字列の終端記号も含んだ、バッファの大きさ」を指定するので80で良い筈ですが、たま~に「終端記号を含まない、読み込む最大長」と勘違いしている人がいます。



それよりも問題なのは
 if(!feof(fp)) printf(str);
の行のprintfです。これは
 if(!feof(fp)) printf("%s",str);
と書かないと、思わぬバグを引き起こします。

試しに、1行入力の入力時に「aaa%s\naaaa\n」と入力してみましょう。unix環境ではcore dumpしますし、Windows環境だと例外処理で強制終了したり、Abnormal program terminationの表示が出て停止します。

例えば、キーボードで文字チャットが出来るMMOタイプの某オンラインゲームは、チャット文字中に「%s」ってのを入れると、ゲーム本体が例外を出して停止しますが「とても恥かしいバグを出した」って事で有名になりました。

この回答への補足

お礼の訂正:

誤:aaa \naaaa\n
正:aaa(null)\naaaa\n

補足日時:2007/12/11 15:58
    • good
    • 0
この回答へのお礼

回答ありがとうございます!
ご指摘のとおりに実行してみましたが、エラーが出ません。

aaa \naaaa\n

と表示されるだけです。
ちなみに、「Visual Studio .NET 2003 コマンドプロンプト」で実行しました。
それでもやっぱり、printf("%s", str); としたほうが良いですか?

お礼日時:2007/12/11 15:53

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