質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

Q&A

解決済

3回答

946閲覧

fgets関数が標準入力から受け取る末尾文字について教えてください

TeraTera

総合スコア3

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

0グッド

0クリップ

投稿2023/02/02 05:25

編集2023/02/02 06:02

実現したいこと

fgets関数で受け取る末尾文字について理解する

前提

バッファリングについて学んでいるのですが、以下のfgets関数で標準入力を受け取って、バッファリングを行う際の処理内容がいまいち掴めず、困っています。

発生している問題・エラーメッセージ

「該当のソースコード」欄に、ソースコードの全文を載せさせて頂きました。
その中で、よくわからないのが、以下の部分です。

if (p != NULL) *p = '\0'; if(buf[0] == '\0') break; printf("%u: %s", line, buf); line ++;

例えば、下記のコードをコンパイル、実行、abcdefghijklmnという文字列を入力したとします。
そうすると、以下のような実行結果になりますよね。

abcdefghijklmn (<-- 入力した内容)
0: abcd1: efgh2: ijkl3: mn

bufは5つ配列の要素を受け取り、0~3のそれぞれ末尾文字として\0がつくため、4文字づつ出力され、処理が終わるところまでは理解できました。

ですが、4回目の処理について違和感があります。
mnが出力される場合、buf[]配列は以下のようになっている認識です。

m n \n \0

ここでstrcharが\nを見つけるので、それを\0に変更。なので、実際にprintfする際は、以下のようになっていると思います。

m n \0 \0

printfで出力する際は、\0時点で末尾文字が入っているため、m n \0 まで出力され、最後の\0はbuf[0]に格納され、後続のbreak処理に入るという理解でいいのでしょうか?

該当のソースコード

#include <stdio.h> #include <stdlib.h> #include <string.h> int main(void) { char buf[5]; unsigned int line = 0; for(;;) { if((fgets(buf, sizeof buf, stdin)) == NULL) { fputs("読み込み中にエラーが発生しました", stderr); exit(1); } /*末尾が\nの場合、\0に置き換える*/ char *p = strchr(buf, '\n'); if (p != NULL) *p = '\0'; if(buf[0] == '\0') break; printf("%u: %s", line, buf); line ++; } }

試したこと

ここに問題に対して試したことを記載してください。

補足情報(FW/ツールのバージョンなど)

ここにより詳細な情報を記載してください。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

1T2R3M4

2023/02/02 05:45

>以下のような実行結果になりますよね。 提示のコードではならないと思います。 違うコードを実行していませんか。
TeraTera

2023/02/02 06:03

すみません、見やすく実行結果を改行したつもりだったのですが、余計でしたね。 実行結果修正しました。
int32_t

2023/02/02 06:46

> 後続のbreak処理に入るという理解でいいのでしょうか? 入力が abcdefghijklmn <Enter> だと、break しないしプログラムは終わらないですよね。 もう一度 <Enter> を押すと break して終わると思います。
jimbe

2023/02/02 07:16 編集

一通り表示されたらプログラムが終わっていると思われているんですね。 終わっている→break している→buf[0]が'\0'になっている→どういうこと? と。 それが一番の原因かもしれません。
TeraTera

2023/02/02 07:17 編集

あー!私の勘違いでしたね 確かに、Enterを押して、その後に処理が終了していました・・・ Enterを押す → buf[0]が\nを受け取る → 後続のstrchrで\0に変換される → if文でbreak しているんですね。 ベストアンサーにしたいのですが、コメントなのでできず、すみません!
int32_t

2023/02/02 07:17

では回答を書いておきます。
TeraTera

2023/02/02 07:19

int32_tさんありがとうございます!BAにします
guest

回答3

0

ベストアンサー

abcdefghijklmnという文字列を入力したとします。

abcdefghijklmn <Enter> と入力した場合、最後に mn\n\0mn\0\0 を表示した後にまた fgets() に入ります。ここで追加の入力がなければ fgets() は返ってきません。よって、break もしませんしプログラムは終了しません。もう一度 <Enter> を入力すると buf[0]\n が入るので break します。

このコードは、

  • 1行の文字数が4の倍数(0文字も含む) + <Enter> だと4文字ずつ表示した後で break して終了。
  • 1行の文字数が4の倍数ではない場合、4文字ずつ表示した後て終了しない。次の入力を処理する。
  • EOF が現れたら、"読み込み中にエラーが発生しました" と表示して終了。

という挙動になります。

投稿2023/02/02 07:30

int32_t

総合スコア20843

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

TeraTera

2023/02/02 07:40

ありがとうございました!ようやく理解できました!!
guest

0

printfで出力する際は、\0時点で末尾文字が入っているため、m n \0 まで出力され、最後の\0はbuf[0]に格納され、後続のbreak処理に入るという理解でいいのでしょうか?

いいえ。現行コードでは「abcdefghijklmn\n」の場合、breakしていません。

もし貴方がcmd等のCUI上で実行している場合、[3: mn]表示後「入力待機状態」になっているはずです。

paiza.ioを代表とした実行時にあらかじめinputを設定しておく系の外部サイトなどで実行すればエラー終了することが確認できると思います。

投稿2023/02/02 07:17

pig_vba

総合スコア807

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

m n \0 まで出力され、最後の\0はbuf[0]に格納され、

ココがおかしいです。
"最後の\0" を buf[0] に入れるという処理は見当たりません。
printf が、パラメータに指定されたデータを(表示したからと)操作することもありません。

動かせるコードなのですから、想像するだけでなく実際に動作させて調査しては如何でしょうか。

投稿2023/02/02 05:46

編集2023/02/02 07:08
jimbe

総合スコア12632

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

TeraTera

2023/02/02 06:06

つまり、最後のprintf()では、以下が出力されており、buf配列の要素は無くなっているということでしょうか? m n \0 \0 buf[0]に\0が入ってbreakするまでの流れが分からないんですよね・・・ 実際に動かしてみると動くのですが、どうしても理解したいので。
jimbe

2023/02/02 07:03 編集

>buf配列の要素は無くなっている ですから、要素を無くす処理を書いていなければ、無くなることはありません。 fgets が標準入力から取り出すと標準入力からデータが無くなるのと同じように printf が表示したらその元になった変数からデータが無くなるとお考えでしたら、それが間違いです。 printf の前が「m n \0 \0」なら、 printf の後も「m n \0 \0」のままです。 コード一行おきに printf("1\n") とか printf("2\n") とかマークを表示するようにして、プログラムがどう動いているのかを確認してはどうでしょうか。
TeraTera

2023/02/02 07:13

書き方が悪かったですね。 printf()で出力されて、「fgetsでstdinを受け取るという、後続の処理の後に」という意味でした。 自分で調査してみます〜
jimbe

2023/02/02 07:23

入力 (fgets 等) の直前に printf("入力: ") と入れておいたり、プログラム終了時に printf("終了\n") と入れておいたりで、今後はプログラムがどういう状況か分かるように予防処置をしておくと良いかもしれませんね。
TeraTera

2023/02/02 07:49

なるほど、今後自分で状況を整理できるような方法も学んでいきます!ありがとうございます!!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問