「私はなんにも知りません。」

しかし、伸びていく方向に陽は当たるようです。

C言語でchar型配列にASCIIコードを入力したら意図せぬ動きになった

今更C言語の初歩で躓く

4月から会社に入り、まず新人研修があります。
その新人研修中でC言語を用いたプログラミングがあり、様々な演習を解いていく時間があります。
そこでちょっと不意を突かれて躓いたところがあったので、個人的忘備録として紹介しておきます。

一部改変してありますが、以下問題文です。

char型の配列を用意し、そこにscanfを用いてASCIIコードを10文字分入力して、それを文字列として表示させなさい。

なにも考えずに私が書いたコードが以下の通りです。

#include <stdio.h>
#define N 10 //文字数

int main(void){
    char string[N + 1] = {0};
    int i;

    for(i = 0; i < N; i++){
        printf("%d文字目の入力 => ", i + 1);
        scanf("%d", &string[i]);
    }

    printf("%s\n", string);

    return 0;

}

これを実行してみると、

C:>a  
1文字目の入力 => 65  
2文字目の入力 => 66  
3文字目の入力 => 67  
4文字目の入力 => 68  
5文字目の入力 => 69  
6文字目の入力 => 70  
7文字目の入力 => 71  
8文字目の入力 => 72  
9文字目の入力 => 73  
2文字目の入力 => 74  
3文字目の入力 => 75  
4文字目の入力 =>

あれ?なぜか9文字目の次に2文字目の入力が続いている…?

原因

最初はループの範囲設定がおかしいだとか考えましたが、そうではなく、char型の変数にint型としてASCIIコードを代入することがまずかったようです。
というのも、scanfが%dで受けている以上、入力された値の型はintであり、このコンパイラでは4byteの変数です。
一方その入力値を代入しようとしている変数はcharであり、1byteの変数です。
4byteの値を無理やり1byteの変数に格納するものですから、メモリ上でそばにいるインデックスであるiの値を書き換えてしまったため、ループを抜けなくなったというのが、この現象の原因のようです。

解決策

解決策としては、int型の入力値をいったん別なint型の変数に格納し、それを明示的にchar型にキャストしてから代入することで、期待した動作をしてくれます。

#include <stdio.h>
#define N 10 //文字数

int main(void){
    char string[N + 1] = {0};
    int a, i;

    for(i = 0; i < N; i++){
        printf("%d文字目の入力 => ", i + 1);
        scanf("%d", &a);

        string[i] = (char)a; //ここで明示的にキャスト
    }

    printf("%s\n", string);

    return 0;

}

以下実行結果

C:>a
1文字目の入力 => 65
2文字目の入力 => 66
3文字目の入力 => 67
4文字目の入力 => 68
5文字目の入力 => 69
6文字目の入力 => 70
7文字目の入力 => 71
8文字目の入力 => 72
9文字目の入力 => 73
10文字目の入力 => 74
ABCDEFGHIJ

サイズの大きな型から小さな型に暗黙的にキャストされる際は特に気をつけておかないと、予期せぬことが起こってしまういい例でした。