scanfの謎

##問題

カード枚数と各々のカードの記号と数字を入力して
カードの情報を全て出力するという、とても簡単なプログラム

以下のように実装したのだが...

```C++:cards.cpp
#include <cstdio>

//記号と数字を持つCard
struct Card {
    char suit;
    int num;
};

Card card[100000];

int main () {
    int n;
    scanf("%d", &n);

    for (int i = 0; i < n; i++) {
        scanf("%c %d", &card[i].suit, &card[i].num);
    }

    printf("点呼取ります！！\n");
    for (int i = 0; i < n; i++) {
        printf("card[%d]の記号は%cで,数は%dです\n", i, card[i].suit, card[i].num);
    }
    return 0;
}
```

```text:入力1
3
a 1
b 2
c 3
```

```text:実行結果
点呼取ります！！
card[0]の記号は
で,数は0です
card[1]の記号はaで,数は1です
card[2]の記号は
で,数は0です
```

あれ？？

```text:入力2
2
c 3
v 4
```

```text:実行結果
点呼取ります！！
card[0]の記号は
で,数は0です
card[1]の記号はcで,数は3です
```

もはやぐちゃぐちゃ(笑)

* \n入れてないのに変なところで改行してしまっている
* card[1]以外の時に値が格納されていない
* card[1]にcard[0]の値が格納されている


##n=2にしてみる
```C++
#include <cstdio>

//記号と数字を持つCard
struct Card {
    char suit;
    int num;
};

Card card[2];

int main () {
    //int n;
    //scanf("%d", &n);

    for (int i = 0; i < 2; i++) {
        printf("%d番目\n", i);
        scanf("%c %d", &card[i].suit, &card[i].num);
        printf("card[%d].suitに%cを代入,numに%dを代入します\n", i, card[i].suit, card[i].num);
    }

    printf("点呼取ります！！\n");
    for (int i = 0; i < 2; i++) {
        printf("card[%d]の記号は%cで,数は%dです\n", i, card[i].suit, card[i].num);
    }
    return 0;
}
```

```text:実行結果
0番目
a 1
card[0].suitにaを代入,numに1を代入します
1番目
b 1
card[1].suitに
を代入,numに0を代入します
点呼取ります！！
card[0]の記号はaで,数は1です
card[1]の記号は
で,数は0です
```

今度はcard[0]のときだけ、きちんと格納されている

* card[1]のとき値が格納されていない
* \nを入れていないのに変なところで改行している

##カードの入力処理だけiostreamで書いてみる

```C++
#include <iostream>
#include <cstdio>

//記号と数字を持つCard
struct Card {
    char suit;
    int num;
};

Card card[100000];

int main () {
    int n;
    scanf("%d", &n);

    for (int i = 0; i < n; i++) {
        printf("%d番目\n", i);
        //scanf("%c %d", &card[i].suit, &card[i].num);
        std::cin >> card[i].suit >> card[i].num;
        printf("card[%d].suitに%cを代入,numに%dを代入します\n", i, card[i].suit, card[i].num);
    }

    printf("点呼取ります！！\n");
    for (int i = 0; i < n; i++) {
        printf("card[%d]の記号は%cで,数は%dです\n", i, card[i].suit, card[i].num);
    }
    return 0;
}
```

```text:実行結果
5 
0番目
a 1
card[0].suitにaを代入,numに1を代入します
1番目
b 2
card[1].suitにbを代入,numに2を代入します
2番目
c 3
card[2].suitにcを代入,numに3を代入します
3番目
d 4
card[3].suitにdを代入,numに4を代入します
4番目
e 5
card[4].suitにeを代入,numに5を代入します
点呼取ります！！
card[0]の記号はaで,数は1です
card[1]の記号はbで,数は2です
card[2]の記号はcで,数は3です
card[3]の記号はdで,数は4です
card[4]の記号はeで,数は5です
```

成功!!
これで分かったことは

```C++
scanf("%c %d", &card[i].suit, &card[i].num);
```

この1行に問題があるということ。

##ひっくり返してみた
```C++
#include <cstdio>

//記号と数字を持つCard
struct Card {
    char suit;
    int num;
};

Card card[100000];

int main () {
    int n;
    scanf("%d", &n);

    for (int i = 0; i < n; i++) {
        printf("%d番目\n", i);
        //数字→記号の順に入力
        scanf("%d %c", &card[i].num, &card[i].suit);
        printf("card[%d].suitに%cを代入,numに%dを代入します\n", i, card[i].suit, card[i].num);
    }

    printf("点呼取ります！！\n");
    for (int i = 0; i < n; i++) {
        printf("card[%d]の記号は%cで,数は%dです\n", i, card[i].suit, card[i].num);
    }
    return 0;
}
```

```text:実行結果
3
0番目
1 a
card[0].suitにaを代入,numに1を代入します
1番目
2 b
card[1].suitにbを代入,numに2を代入します
2番目
3 c
card[2].suitにcを代入,numに3を代入します
点呼取ります！！
card[0]の記号はaで,数は1です
card[1]の記号はbで,数は2です
card[2]の記号はcで,数は3です
```

成功!

##分けてみた

```C++
#include <cstdio>

//記号と数字を持つCard
struct Card {
    char suit;
    int num;
};

Card card[100000];

int main () {
    int n;
    scanf("%d", &n);

    for (int i = 0; i < n; i++) {
        printf("%d番目\n", i);
        scanf("%c", &card[i].suit);
        scanf("%d", &card[i].num);
        printf("card[%d].suitに%cを代入,numに%dを代入します\n", i, card[i].suit, card[i].num);
    }

    printf("点呼取ります！！\n");
    for (int i = 0; i < n; i++) {
        printf("card[%d]の記号は%cで,数は%dです\n", i, card[i].suit, card[i].num);
    }
    return 0;
}
```

```text:実行結果
3  
0番目
a 1
card[0].suitに
を代入,numに0を代入します
1番目
card[1].suitにaを代入,numに1を代入します
2番目
b 2
card[2].suitに
を代入,numに0を代入します
点呼取ります！！
card[0]の記号は
で,数は0です
card[1]の記号はaで,数は1です
card[2]の記号は
で,数は0です
```

##%sを使う
```C++
3
0番目
a 1
card[0].suitにaを代入,numに1を代入します
1番目
b 2
card[1].suitにbを代入,numに2を代入します
2番目
c 3
card[2].suitにcを代入,numに3を代入します
点呼取ります！！
card[0]の記号はaで,数は1です
card[1]の記号はbで,数は2です
card[2]の記号はcで,数は3です
```

成功!
ちなみに入力のときのみ%sを使う。
出力時にprintf("%s", card[i].suit);とするとエラーとなる。

##%sもiostreamも使いたくないとき

```C++
#include <cstdio>

//記号と数字を持つCard
struct Card {
    char suit;
    int num;
};

Card card[100000];

int main () {
    int n;
    scanf("%d", &n);

    for (int i = 0; i < n; i++) {
        printf("%d番目\n", i);
        scanf(" %c %d", &card[i].suit, &card[i].num);
    }

    printf("点呼取ります!!\n");
    for (int i = 0; i < n; i++) {
        printf("card[%d]のnumは%dで, suitは%cです\n", i, card[i].num, card[i].suit);
    }
    return 0;
}
```

```text:実行結果
3
0番目
a 1
1番目
b 2
2番目
c 3
点呼取ります!!
card[0]のnumは1で, suitはaです
card[1]のnumは2で, suitはbです
card[2]のnumは3で, suitはcです
```

%cの前に空白を入れるだけでなぜかいける

##最後に
scanf(cstdio)よりcin(iostream)が推奨されるのは,今回のように
cinの方が~~バグ~~が少ないのが一つの理由。
*バグじゃなくてコードミス*
iostreamの方が安全で使いやすい。
[詳細]
https://code.i-harness.com/ja/q/2bd4df