次のプログラムは入力された行を読み込み、コマンドラインで指定されたファイルに書き込みます。
空白行が入力されたら、入力の終了とみなしてファイルを閉じます。続いてファイルを入力用に開き、
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でも良いような気がするのですが・・・
No.1ベストアンサー
- 回答日時:
char str[80];
で文字列用の配列を80バイト分確保していますが文字列の終端には'\0'が必要です。
そのため実際に使える文字列の長さは79になります。
それを意識して
fgets(str, 79, fp);
としたのだと思いますがfgetsの引数は'\0'を含んだ数なので
fgets(str, 80, fp);
で問題ないでしょう。
さっそくの回答ありがとうございます!
fgets(str, 80, fp);
にしても問題なく実行できたのでおかしいな?と思って質問しました。
私が学習している本に、
『char *fgets(char *文字列, int 数値, FILE *ストリーム);
fgets()関数は、「ストリーム」に結び付けられているファイルから文字を読み込み、
「文字列」が指す文字列に格納していきます。この操作は「数値」マイナス1個分
の文字を読み込むか、改行文字に出会うか、あるいはファイルの終わりに達するまで
続けられます。いずれの場合も、格納された文字列の後にヌル文字が追加されます。
類似のgets()と違って、改行文字も格納されます。』
とあります。
また質問で恐縮なんですが、「数値」は何のためにあるのでしょうか?
あと、改行文字が格納されることと数値が79なのは無関係ですか?
未熟者の私ですが、どうか教えてください。お願いします。
No.5
- 回答日時:
>ご指摘のとおりに実行してみましたが、エラーが出ません。
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)」を使う事です。
質問に答えてくださってありがとうございます!!
大変勉強になりました!
これからはprintf(str); のような書き方に気をつけていこうと思います。
丁寧に回答してくださってありがとうございました。
No.4
- 回答日時:
> fputs()関数はファイルに書き込むための関数だから、表示できないのでは?
ファイルというよりはストリームに書き込むための関数ですね。
printfもストリームに書き込むための関数です。そして、printfを使ったとしても、「表示」できるかどうかは環境に依存します。
ちなみに、printfの出力先はstdoutです。ですから、fputsでstdoutを指定すれば、printfと出力先が同じになります。
回答ありがとうございます!
fputs(str, fp);
に勝手に変えてました。ごめんなさい!
>ちなみに、printfの出力先はstdoutです。ですから…
この文章を読んでからfputs(str, stdout)にしたら表示できました!
大変勉強になりました。ありがとうございました。
No.2
- 回答日時:
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
と表示されるだけです。
ちなみに、「Visual Studio .NET 2003 コマンドプロンプト」で実行しました。
それでもやっぱり、printf("%s", str); としたほうが良いですか?
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- C言語・C++・C# プログラミングの授業の課題です 1 2023/01/17 22:15
- C言語・C++・C# このプログラミング誰か教えてくれませんか 1 2022/06/02 15:27
- C言語・C++・C# バイナリファイルをコピーするのにかかる時間を測りたいのですが実行するとFatel error:gli 2 2022/11/03 01:10
- C言語・C++・C# c言語の問題の説明、各所ごとに 5 2023/07/26 11:03
- C言語・C++・C# c言語 プログラムのエラー 1 2023/02/11 20:31
- C言語・C++・C# 宣言する関数の形が決まっている状態で、 str1とstr2の文字列をこの順に引っ付けてstrに保存し 2 2022/05/30 18:21
- C言語・C++・C# c言語配列の結合についてです。 なぜうまくいかないのでしょうか。 #include <stdio.h 4 2022/05/30 22:42
- C言語・C++・C# C言語のファイル入力が分かりません 2 2022/05/22 06:35
- C言語・C++・C# c言語の問題です 3 2023/01/10 16:15
- C言語・C++・C# str[j++]の意味 2 2022/08/30 16:20
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
Enterキーを押されたら次の処理...
-
Cでファイルの行数をカウントす...
-
構造体メンバの初期化
-
C言語のプログラムで、途中で止...
-
プログラミングの授業の課題です
-
C言語で複数列のデータを1列の...
-
fread(),fwrite()等について
-
[C]セグメンテーションエラー:...
-
#defineが使用するメモリ領域に...
-
C++で指定文字列のカウント方法...
-
printf による16進表示について
-
fscanfの使い方
-
マイナスからプラスへ転じた時...
-
「指定されたキャストは有効で...
-
数字以外が入力されたらエラー...
-
C言語で簡単なパックマンゲーム...
-
比較回数と交換回数表示について
-
C言語での引数の省略方法
-
2÷3などの余りについて
-
4の倍数を論理演算で表す。。
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
Enterキーを押されたら次の処理...
-
C言語で複数列のデータを1列の...
-
#defineが使用するメモリ領域に...
-
printf による16進表示について
-
空白を含んだ文字列がうまく格...
-
C言語のプログラムで、途中で止...
-
プログラミングの授業の課題です
-
char型2つを結合し、short型に...
-
C言語でのCSVファイルの読み出...
-
矢印キーを押下してコンソール...
-
【C言語】全角文字の配列を、全...
-
エラーについて質問です。
-
リストの作成と出力(C言語)
-
バイナリファイル(画像)のよみ...
-
[C]セグメンテーションエラー:...
-
WinInetのInternetOpenUrl関数...
-
VC++でSQLへSELECT文を送ったの...
-
受信データから必要な部分のみ...
-
c言語で文書を読み込み、単語の...
-
reallocでエラー
おすすめ情報