[C言語]printfの後の入力待ちで入力バッファがクリアされる理由

C言語の入門者向けに書かれた書籍を見ると、以下のようなプログラムが良く有ります。

#include<stdio.h>
int main()
{
    int value;
    printf( "数字を入力して下さい:" );
    scanf( "%d", &value );
 
    printf( "入力値は%dです\n", value );
    return 0;
}



このコード、今まで特に気にしてこなかったのですが、良く考えてみるとちょっと疑問に思うところが出てきました。

printf()は入力をバッファリングしているので、指定した文字列が直ぐに表示されるとは限りません。
そうすると、scanfの前にprintfの文字が出力されるとは限らず,厳密には以下のコードのようにすべきでは?
というのが疑問点です。

    printf( "数字を入力して下さい:" );
    fflush( stdout );                // バッファのフラッシュが必要では?
    scanf( "%d", &value );



ちなみに、上記のコードをlinux+gccでコンパイルしてgdbでデバッグしてみると、printfでは文字が表示されず、次のscanfで出力されている事は確認済みです(Cプログラマにとっては常識レベルの事ですが,念のため)。

以下、確認結果です。

$ gcc -O0 -g -o inputcheck inputcheck.c
$ gdb inputcheck
GNU gdb (Ubuntu/Linaro 7.3-0ubuntu2) 7.3-2011.08
Reading symbols from /home/user1/work/inputcheck...done.
(gdb) list
1   #include<stdio.h>
2   int main()
3   {
4       int value;
5       printf( "数字を入力して下さい:" );
6       scanf( "%d", &value );
7   
8       printf( "入力値は%dです\n", value );
9       return 0;
10  }
(gdb) break 5
Breakpoint 1 at 0x804843d: file inputcheck.c, line 5.
(gdb) start
Temporary breakpoint 2 at 0x804843d: file inputcheck.c, line 5.
Starting program: /home/user1/work/inputcheck 
 
Breakpoint 1, main () at inputcheck.c:5
5       printf( "数字を入力して下さい:" );
(gdb) next                                                 <= ここで表示されずに...
6       scanf( "%d", &value );
(gdb) next                                                 <= scanf()のタイミングで出る
数字を入力して下さい:10
8       printf( "入力値は%dです\n", value );
(gdb) continue
Continuing.
入力値は10です
[Inferior 1 (process 7573) exited normally]
(gdb)



この辺のところはかなり細かな話で、市販の書籍だと適当な事が書いてある危険があるので、規格書であるJIS Cで確認してみる事にします(本当はANSI Cでチェックしたかったのですが、手元に無いので)。

以下、関連する箇所をC言語の規格書であるJIS X3010より引用します。
JIS規格のドキュメントはhttp://www.jisc.go.jp/app/JPS/JPSO0020.htmlより閲覧できます。

5.1.2.3 プログラムの実行
*中略*
対話型装置に対する入出力動作は7.19.3で規定する通りに行う。この要求は、入力要求メッセージ
が実際にプログラムの入力待ち以前に現れることを確実にするため、バッファリングされない出力
又は、行バッファリングされた出力ができる限り早く現れることを意図する。



“…意図する”という所が知りたい情報です。
入出力動作は7.19.3で規定って事なので、そちらも見てみます。

7.19.3 ファイル
*中略*
ストリームをバッファリングしていない(unbuffered)場合,
    文字が入力もとから又は出力先へ可能な限りすぐに表れることを意図する。
    そうでない場合、文字を貯めているホスト環境へまたはホスト環境からブロック単位で転送してもよい。
 
ストリームを完全バッファリングしている(fully buffered)場合,
    バッファが一杯になった時点で,ホスト環境へまたはホスト環境から文字をブロック単位することを意図する。
 
ストリームを行バッファリングしている(line buffered)場合、
    改行文字に達した時点で、ホスト環境へまたはホスト環境から文字をブロック単位することを意図する。
 
 
さらに、次に掲げる三つの場合、ホスト環境へ文字をブロック単位で転送することを意図する。
 -バッファがいっぱいになった場合
 -バッファリングしていないストリームに対して入力要求があった場合
 -行バッファリングしているストリームに対して、ホスト環境からの文字転送を必要とする入力要求があった場合
 
これらの特性のサポートは処理系定義とし、setbuf関数およびsetvbuf関数の影響を受けてもよい。



というわけで、JIS C的には、scanfのタイミングでstdoutの出力バッファがクリアされるのは、JIS X3010の7.19.3の以下の記述で規定されていました。

さらに、次に掲げる三つの場合、ホスト環境へ文字をブロック単位で転送することを意図する。
 -行バッファリングしているストリームに対して、ホスト環境からの文字転送を必要とする入力要求があった場合



ただ、その後に”これらの特性のサポートは処理系定義”という記述もあります。
なので、厳密に言えば 事前にfflush()した方が確実なのかもしれません。

入門ANSI‐C

関連記事

コメントを残す

メールアドレスが公開されることはありません。