キーボード入力

ここでは,キーボードからの入力方法の詳細を説明する.

注意:下記の標準出力関数を使用するためには, #include <stdio.h> が必要です.

1文字入力

キーボードから1文字入力し,変数に代入するには,getchar関数を用いる.
関数のプロトタイプは,int getchar();

#include <stdio.h>
	
int main(void)
{
    char c = getchar();

    printf("input char : ");

    printf("%d\n", c);
	
    return 0;
}
実行例:
input char : a
97
input char : s
115

1文字+改行を入力すると,入力された文字の文字コードが表示される.

1行入力

キーボードから1行(=改行まで)入力するには,gets関数を用いる(とされてきた).
関数のプロトタイプは,char *gets(char *buffer);であり,キー入力された文字をbufferに格納する.
ところが,このプロトタイプからも分かる通り,getsは,バッファを溢れさせることが可能なため,この関数の使用は推奨されないか,または使用できなくなっている.

代わりに,ファイル入出力で用いるfgets関数を用い,入力元を stdinと指定すれば良い.

または,char *gets_s(char *buffer, size_t bufsize);関数が利用可能な場合がある. (この関数は,全ての処理系で利用できるわけではない).

#include <stdio.h>

int main(void)
{
    const int N = 10;  /* 入力バッファサイズ */
    char buf[N];       /* 入力バッファ */

    fgets( buf, N, stdin);    /* bufのサイズを指定できる. stdin = キーボード */

    /* 文字列を表示 */
    printf("%s\n", buf);

    /* バッファ内データを文字コードで表示 */
    for(int i=0; i<N; i++) {
        printf("%02x ", buf[i]);
    }

    return 0;
}
実行例1(上が入力,下が出力)
01234
01234
                          <- 改行コード
30 31 32 33 34 0a 00 ffffffd0 40 00

実行例2(上が入力,下が出力)
0123456789
012345678                 <- N-1文字分,格納された
30 31 32 33 34 35 36 37 38 00

このように,入力バッファのサイズを超える入力は打ち切られるため,バッファがあふれることはない.
また,ヌル文字を代入するため,実際に入力できる文字数はバッファサイズ-1である.
もう一つ,改行コードがそのまま入力バッファに格納される点に注意.

scanf関数の使い方

単純な文字や文字列の入力だけではなく,入力された文字列を「数値」として格納したい場合や,書式を指定してキーボード入力を処理したい場合にはscanf関数を用いる.

scanf 関数は,printf 関数によく似ており,書式指定文字列に続けて変数を渡すのだが, 値を格納する変数の前に & をつける必要がある.
この記号は,変数のアドレスを取り出す演算子で,scanf関数に値を格納する「場所」を教えている.
したがって,元々アドレスを意味しているポインタ変数や配列名の場合は,& をつける必要がない.(というより,つけてはいけない!)

#include <stdio.h>
 
int main(void)
{
    int a;
    float b;
    double c;
 
    printf("a = ");
    scanf("%d", &a);    // ここは整数型
    
    printf("b = ");
    scanf("%f", &b);    // ここは単精度実数型
 
    printf("c = ");
    scanf("%lf", &c);    // ここは倍精度実数型
 
    printf("a*a = %d \n", a*a);
    printf("b*b = %f \n", b*b);
    printf("c*c = %lf \n", c*c);
    return 0;
}

一行に複数の変数を入力する方法

キーボード入力の際に,1行で複数のデータを入力する方法である.
そのような場合は,

scanf関数の書式指定に複数の % 記号を並べる.
ただし,ユーザに書式通りに入力させるよう指示する必要がある.

#include <stdio.h>
 
int main(void)
{
    int a, b;
 
    printf("a b =? ");
    scanf("%d %d", &a, &b);      /* スペース区切りで整数を2つ入力させる */

    printf("a = %d  b = %d\n", a, b);
    
    return 0;
}
実行例:(OK)
a b =? 1 2
a = 1  b = 2

実行例:(NG)
a b =? 1,2
a = 1  b = 0

scanfの戻り値

scanf関数のプロトタイプ宣言を見ていると,戻り値が整数であることがわかる.
戻り値はscanfが変換・代入に成功した変数の個数を表している.
書式の不一致など,何らかの原因で変数への代入に失敗すると,渡した変数の個数より少ない値が返される.

#include <stdio.h>
 
int main(void)
{
    int a, b;
    int ret;      /* scanfの戻り値用 */

    printf("a b =? ");
    ret = scanf("%d %d", &a, &b);      /* スペース区切りで整数を2つ入力させる */

    printf("a = %d  b = %d\n", a, b);
    printf("ret = %d\n", ret);
    
    return 0;
}
実行例:(OK)
a b =? 100 200
a = 100  b = 200
ret = 2

実行例:(NG)
a b =? 100,200
a = 100  b = 0
ret = 1               <-1つしか変換されなかったことが検出できる.

a b =? 1 a
a = 1  b = 256
ret = 1               <-1つしか変換されなかった.

C:\Users\tatsuya>test
a b =? a b
a = 4264008  b = 256
ret = 0               <-2つとも数値として変換されなかった.

文字列の入力

以下は文字列の入力方法のまとめである。 「文字,文字列」,「配列」を先に理解しておくと良い.

#include <stdio.h>
 
int main(void)
{
    char str1[100];
    char str2[100];
	
    printf("Input string 1 :");
    scanf("%s", str1);

    printf("Input string 2 :");
    scanf("%s", str2);

    printf("str1 = %s \n", str1);
    printf("str2 = %s \n", str2);
	
    return 0;
}

この例では,文字列を2つ入力させている.
一見,なんの問題もないように見えるが,実行すると以下のような動作をする.

(問題のない例)
Input string 1 :aaa
Input string 2 :bbb
str1 = aaa 
str2 = bbb 

(問題の発生する例)
Input string 1 :aaa bbb
Input string 2 :str1 = aaa 
str2 = bbb

問題の発生する例では,最初にスペースを含む文字列を入力すると,2回目のscanf入力は飛ばされてプログラムが終了してしまう.
この原因は,%sでは,入力している文字列にスペースが含まれると,そこで単語として区切られてしまい,文字列strにはスペース以前の文字しか代入されないからである.

つまり,aaa bbb(改行) というキー入力に対して,一旦,「入力バッファ」と呼ばれるメモリーに文字列が全部格納され,次にscanf関数が書式指定文字列を元に入力バッファの中身を解析をして,ここではスペース文字までの 「aaa」 のみが変数str1に代入される.
残りの「bbb」+「改行」 が入力バッファに残っている状態で,2回目のscanfが呼ばれ,もともと入力バッファに残っていた「bbb」が str2 に代入されてしまう.

ではどうすれば良いかというと,

#include <stdio.h>
 
int main(void)
{
    char str1[100];
    char str2[100];
	
    printf("Input string 1 :");
    scanf("%[^\n]%*c", str1);

    printf("Input string 2 :");
    scanf("%[^\n]%*c", str2);

    printf("str1 = %s \n", str1);
    printf("str2 = %s \n", str2);
	
    return 0;
}

とすれば良い.

実行例:
Input string 1 :aaa bbb
Input string 2 :ccc ddd
str1 = aaa bbb 
str2 = ccc ddd

ここで,%[^\n]%*c のうち, %[^\n]改行コードまでの(スペースも含めた)文字を読み込むという意味であり, %*cは,一文字(ここでは改行コードを)読み飛ばす,という動作を指示したことになる.
scanf()関数は,多くのC言語入門書の最初に登場するライブラリ関数だが,実はかなり高度で複雑な機能を持っている.

もう一つの方法として,ファイルから1行読み込むライブラリ関数fgets()を使用し,ファイルポインタとしてstdinを指定する方法がある. stdinは標準入力=キーボードを表し,何もしなくても使用することができる.

#include <stdio.h>
 
int main(void)
{
    char str1[100];
    char str2[100];
    
    printf("Input string 1 :");
    fgets(str1, 100, stdin);

    printf("Input string 2 :");
    fgets(str2, 100, stdin);

    printf("str1 = %s \n", str1);
    printf("str2 = %s \n", str2);
    
    return 0;
}
実行例:
Input string 1 :aaa bbb
Input string 2 :ccc ddd
str1 = aaa bbb

str2 = ccc ddd

この例では,改行まで含めてbufに入力される点が scanf() の例と異なる.