/*01*/ #include <stdio.h>
/*02*/
/*03*/ main()
/*04*/ {
/*05*/ char str1[32], str2[32];
/*06*/
/*07*/ scanf("%s%s", str1, str2);
/*08*/ printf("%s %s\n", str1, str2);
/*09*/
/*10*/ scanf("%s%s", str1, str2);
/*11*/ printf("%s %s\n", str1, str2);
/*12*/
/*13*/ return 0;
/*14*/ }
scanf の変換文字を「%s」とし、第2引数以降に文字列の先頭アドレスを書いています。これで文字列が「'\0'」付きで、指定された配列に代入されます。ここで第二引数について、厳密には指定されたアドレスを先頭とする領域に代入していくのだということは、前章から容易に察しがつくでしょう。
さて、このソースには「文字列を2つ入力するとそれを表示する」という処理が2つあることになります。そしてキーボードから入力する時は、2つの文字列をスペースで区切るか、Enter(改行)キーを押すかします。
Hello(第1文字列) World(第2文字列) Hello(第1文字列) World(第2文字列)
さらに重大な問題があります。2つの文字列しか入力できないはずのところで、誤って3つの文字列を入力してしまったらどうなるでしょう?
Hello(第1文字列) World(第2文字列) abcde(第3文字列)
1度目の入力 Hello World abcde 2度目の入力 programming language
ですから scanf は
scanf("%d%s%d", コード, 商品名, 価格)
scanf に対し、このセクションで使う
/*01*/ #include <stdio.h>
/*02*/
/*03*/ main()
/*04*/ {
/*05*/ char str1[32], str2[32];
/*06*/
/*07*/ gets(str1);
/*08*/ gets(str2);
/*09*/ printf("%s\n", str1);
/*10*/ printf("%s\n", str2);
/*11*/
/*12*/ return 0;
/*13*/ }
gets 関数の使い方は見てのとおり、引数に代入したい配列の先頭アドレスを書くだけですが、引数は1つしか取れません(これも厳密には、指定されたアドレスを先頭とする領域に代入していくわけです)。
また、gets 関数に対応する出力関数として
scanf を使った場合、数値を入力した場合でも文字列同様、誤って余分に入力してしまった分が次の入力時に持ち越されてしまうという問題が起こります。そこで、数字を1度文字列として読みこんでから数値に変換するという方法があります。「atoi」関数は「stdlib.h」に定義されています。
/*01*/ #include <stdio.h>
/*02*/ #include <stdlib.h> /* atoi 関数を使うのに必要 */
/*03*/
/*04*/ main()
/*05*/ {
/*06*/ char str[32];
/*07*/ int x;
/*08*/
/*09*/ gets(str);
/*10*/ x = atoi(str);
/*11*/ printf(" -> %d\n", x);
/*12*/
/*13*/ return 0;
/*14*/ }
まず、数字を文字列として格納しておく char 型配列(str)と、整数にした時の値を格納する int 型の変数 (x) を用意しておきます。
次に、とりあえず gets 関数を使って数字を char 型配列(str)に文字列として格納します。そうしたら、例のようにその文字列を atoi 関数に引数として渡します。すると戻り値として整数が帰ってくるので、int 型変数(x)に格納します。
x = atoi(str);
また、atoi 関数とよく似たものに atof 関数があります。これは、atoi が int 型の数値に直すのに対し、atof は float 型の数値に直します。「atof」関数は「stdlib.h」に定義されています。
/*01*/ #include <stdio.h>
/*02*/
/*03*/ main()
/*04*/ {
/*05*/ char ch1, ch2;
/*06*/
/*07*/ scanf("%c%c", &ch1, &ch2);
/*08*/ printf("%c%c\n",ch1, ch2);
/*09*/
/*10*/ scanf("%c%c", &ch1, &ch2);
/*11*/ printf("%c%c\n", ch1, ch2);
/*12*/
/*13*/ return 0;
/*14*/ }
変換文字を「%c」にし、第2引数以下に変数のアドレスを書きます。文字列の時とほとんど同じです。そして入力する文字はスペースで区切らずに続けて入れます。なお、スペースを入れるとそれも1文字として代入されます。
ab /*「a」が「ch1」に、 「b」が「ch2」に代入される*/
1度目のscanf ab /*ここで Enter*/ 2度目のscanf cd /*ここで Enter*/
もう1つの1文字入力関数は getchar です。「getchar」関数は「stdio.h」に定義されています。
/*01*/ #include <stdio.h>
/*02*/
/*03*/ main()
/*04*/ {
/*05*/ char ch1, ch2;
/*06*/
/*07*/ ch1 = getchar();
/*08*/ ch2 = getchar();
/*09*/ printf("%c%c\n",ch1, ch2);
/*10*/
/*11*/ ch1 = getchar();
/*12*/ ch2 = getchar();
/*13*/ printf("%c%c\n",ch1, ch2);
/*14*/
/*15*/ return 0;
/*16*/ }
使い方は見てのとおり(引数はありません)で、入力された1文字が戻り値として返ってきます。scanf よりもすっきりしていますね。ですから、1文字入力をする時は scanf を使うよりも getchar を使うことのほうが多いようです。しかし、scanf にあった問題はこの関数でもまったく同様に現れます。つまり、Enter も1文字として扱われ、余った分は次の入力に持ち越されてしまうという事です。
また、getchar 関数に対応する出力関数として putchar 関数があります。使い方は putchar 関数の引数に表示したい1文字か、1文字変数を書くだけです(取れる引数は1つだけです)。試しに printf のところを、putchar(ch1); などと書き換えて実行してみてください。あるいは、改行だけして1行あけたい時などには putchar(’\n’); とすると便利かもしれません。「putchar」関数は「stdio.h」に定義されています。
この章でここまでに出てきたキーボードから何かを入力する関数のいくつかは、余分に入力してしまった分が次の入力時まで持ち越されてしまいますね。特に getcharにおいて Enter(エンター:リターン:改行) による「’\n(改行コード)’」まで読みこんでしまうのは問題ですよね。このことに関しては入出力に関する「ストリーム」と呼ばれるものが影響してくるのですが、ここではあまり詳しく説明しません。しかし幸いなことに、この余分なものを破棄してしまう関数があります。それが「rewind」と呼ばれる関数で、本来はファイル操作に使う関数なのですが、このように余分な入力を破棄する働きもあるので活用しましょう。ここで使用する
/*01*/ #include <stdio.h>
/*02*/
/*03*/ main()
/*04*/ {
/*05*/ char str1[32], str2[32];
/*06*/
/*07*/ scanf("%s%s", str1, str2);
/*08*/ printf("%s %s\n", str1, str2);
/*09*/ rewind(stdin);
/*10*/
/*11*/ scanf("%s%s", str1, str2);
/*12*/ printf("%s %s\n", str1, str2);
/*13*/ rewind(stdin);
/*14*/
/*15*/ return 0;
/*16*/ }
ここで rewind 関数の引数
1度目の入力 Hello World abcde 2度目の入力 programming language
ただし、この rewind 関数自体は ANSI で標準搭載されていますが、これを stdin に使ったときにバッファが全て破棄されるという動作は ANSI C で定義されているものではありません。したがって、コンパイラによってはバッファの内容を破棄するどころかバッファの先頭に移動してしまうので、これまでのバッファ内容がすべて復活してしまうこともあります(これは ANSI で規格された rewind の正常な動作です)。私が5つのコンパイラで試したところ、1つはバッファを復活させてしまいました。
そんなときは fseek 関数を試してみてください。この関数に以下の引数を与えて使うとバッファの最後に移動するので、これまでの入力はすでに取り込まれたものとして扱われます。従って見かけ上、先にあげた rewind と同じ効果が期待できます。fseek 関数は stdio.h に定義されています。
fseek(stdin, 0, SEEK_END);
最後に「getchar」関数に関する、よく使う手法を説明しておきます。関数が出てくると先にその関数を処理し、戻り値がそこにあるかのように処理してくれるのはすでに書きましたね。そして getchar 関数は「1文字」を返します。ですから、while 文の条件判断式で直接この関数を書いてしまうのです。
while(getchar() != '\n')
while((c = getchar()) != '\n'){
/*実行文*/
}
while(c = getchar() != '\n') /*間違い*/