C言語のprintf関数でなぜか文字列が文字化けしてしまったときには？

## はじめに
- 技術的な質問やコメント等をくださると大変うれしいです
- LGTMや記事をツイートをしてくださるとモチベーションが上がります

## TL;DR
- C言語で文字列を扱うときヌル終端文字（`\0`）に注意してコーディングすること
- 具体的には「表現したい文字バイト数 + 1バイト」サイズで固定配列を作成すること
- printfで文字化けしたときはヌル終端文字の差し込み忘れを思い出すこと

## 問題の経緯
C言語で文字列を扱いたいとき、基本的には文字配列（ex: `char s[]`）のデータ構造を利用します。JavaScriptやJava、C++といった他のプログラミング言語ではString型のようなデータ型が存在するため、特に気する必要はありませんが、C言語についてはそうはいきません。

与えられた数値を文字列に変換する過程で`printf()`でテストしていたのですが、そのときに出会った「printfが文字化けする問題」をデバッグしたので共有したいと思います。

## 前提の確認
今回問題となった部分のコードは次のようなものです。

### ソースコード
`get_array_size()`でint型の数値から固定文字配列用のサイズを取得して、`convert_int_to_str()`でint型の数値を固定文字配列に挿入するというコードになります。
```c
#include <stdio.h>

// get array size from number for char[]　(number of digits)
int get_array_size(int number)
{
    int size;
    long long tmp;

    size = 0;
    tmp = number;
    if (tmp < 0) 
    {
        size++;
        tmp *= -1;
    }

    while (tmp >= 10)
    {
        tmp /= 10;
        size++;
    }
    size++;
    return size;
}


char *convert_int_to_str(int number, int size, char *str)
{
    int end;
    long long tmp;
    tmp = number;
    
    if (tmp < 0)
    {
        str[0] = '-';
        tmp *= -1;
        end = 1;
    }
    else
        end = 0;

    while (size-- >= end && tmp >= 10)
    {
       str[size] = (tmp % 10) + '0';
       tmp /= 10;
    }
    str[size] = tmp + '0';

    return str;
}

int main(void)
{
    printf("get_array_size():\n");
    printf("        123 : %d\n", get_array_size(123));   // 3
    printf("       -123 : %d\n", get_array_size(-123));   // 4
    printf("          1 : %d\n", get_array_size(9));   // 1
    printf("         10 : %d\n", get_array_size(10));   // 2
    printf("          1 : %d\n", get_array_size(1));   // 1
    printf("          0 : %d\n", get_array_size(0));   // 1
    printf("         -1 : %d\n", get_array_size(-1));      // 2
    printf(" 2147483647 : %d\n", get_array_size(2147483647));   // 10
    printf("-2147483648 : %d\n", get_array_size(-2147483648));   // 11
    printf("\n");

    char s1[3];
    char s2[4];
    char s3[1];
    char s4[2];
    char s5[1];
    char s6[1];
    char s7[2];
    char s8[10];
    char s9[11];
    printf("convert_int_to_str():\n");
    printf("        123 : %s\n", convert_int_to_str(123, get_array_size(123), s1));   // 123
    printf("       -123 : %s\n", convert_int_to_str(-123, get_array_size(-123), s2));   // -123
    printf("          9 : %s\n", convert_int_to_str(9, get_array_size(9), s3));   // 9
    printf("         10 : %s\n", convert_int_to_str(10, get_array_size(10), s4));   // 10
    printf("          1 : %s\n", convert_int_to_str(1, get_array_size(1), s5));   // 1
    printf("          0 : %s\n", convert_int_to_str(0, get_array_size(0), s6));   // 0
    printf("         -1 : %s\n", convert_int_to_str(-1, get_array_size(-1), s7));   // -1
    printf(" 2147483647 : %s\n", convert_int_to_str(2147483647, get_array_size(2147483647), s8));   // 2147483647
    printf("-2147483648 : %s\n", convert_int_to_str(-2147483648, get_array_size(-2147483648), s9));   // -2147483648

    return 0;
}
```

### 実行結果
`convert_int_to_str()`について、ソースコード上のコメント（想定される結果）とは大きく異なることが分かります。`��S�\U`という文字化けや`-21474836482147483647-101109-123123��S�\U`という形でそれまで出力した文字列も含んでしまっています。
```
get_array_size():
        123 : 3
       -123 : 4
          1 : 1
         10 : 2
          1 : 1
          0 : 1
         -1 : 2
 2147483647 : 10
-2147483648 : 11

convert_int_to_str():
        123 : 123��S�\U
       -123 : -123123��S�\U
          9 : 9-123123��S�\U
         10 : 109-123123��S�\U
          1 : 1109-123123��S�\U
          0 : 01109-123123��S�\U
         -1 : -101109-123123��S�\U
 2147483647 : 2147483647-101109-123123��S�\U
-2147483648 : -21474836482147483647-101109-123123��S�\U
```

## 解決策
デバック後は次のようなコードになります。
### ソースコード
具体的な変更点は以下になります。
- `get_array_size()`
    - `size++;` -> `size += 2;`
        - `\0`（ヌル終端文字）用のバイトを用意
- `convert_int_to_str()`
    - ループ変数`i`の導入
    - 文字配列の最後の要素として`\0`の挿入
- `main()`
    - 配列変数`s1` ～ `s9`の配列サイズを`+1`
```c
#include <stdio.h>

// get array size from number for char[]　(number of digits + 1byte)
int get_array_size(int number)
{
    int size;
    long long tmp;

    size = 0;
    tmp = number;
    if (tmp < 0) 
    {
        size++;
        tmp *= -1;
    }

    while (tmp >= 10)
    {
        tmp /= 10;
        size++;
    }
    size += 2;
    return size;
}

// convert data type int to char[]
char *convert_int_to_str(int number, int size, char *str)
{
    int end;
    long long tmp;
    tmp = number;
    
    if (tmp < 0)
    {
        str[0] = '-';
        tmp *= -1;
        end = 1;
    }
    else
        end = 0;

    int i;
    i = size - 2;
    while (i >= end && tmp >= 10)
    {
       str[i] = (tmp % 10) + '0';
       tmp /= 10;
       i--;
    }
    str[i] = tmp + '0';
    str[size - 1] = '\0';
    return str;
}

int main(void)
{
    printf("get_array_size():\n");
    printf("        123 : %d\n", get_array_size(123));   // 4
    printf("       -123 : %d\n", get_array_size(-123));   // 5
    printf("          9 : %d\n", get_array_size(9));   // 2
    printf("         10 : %d\n", get_array_size(10));   // 3
    printf("          1 : %d\n", get_array_size(1));   // 2
    printf("          0 : %d\n", get_array_size(0));   // 2
    printf("         -1 : %d\n", get_array_size(-1));      // 3
    printf(" 2147483647 : %d\n", get_array_size(2147483647));   // 11
    printf("-2147483648 : %d\n", get_array_size(-2147483648));   // 12
    printf("\n");

    char s1[4];
    char s2[5];
    char s3[2];
    char s4[3];
    char s5[2];
    char s6[2];
    char s7[3];
    char s8[11];
    char s9[12];
    printf("convert_int_to_str():\n");
    printf("        123 : %s\n", convert_int_to_str(123, get_array_size(123), s1));   // 123
    printf("       -123 : %s\n", convert_int_to_str(-123, get_array_size(-123), s2));   // -123
    printf("          9 : %s\n", convert_int_to_str(9, get_array_size(9), s3));   // 9
    printf("         10 : %s\n", convert_int_to_str(10, get_array_size(10), s4));   // 10
    printf("          1 : %s\n", convert_int_to_str(1, get_array_size(1), s5));   // 1
    printf("          0 : %s\n", convert_int_to_str(0, get_array_size(0), s6));   // 0
    printf("         -1 : %s\n", convert_int_to_str(-1, get_array_size(-1), s7));   // -1
    printf(" 2147483647 : %s\n", convert_int_to_str(2147483647, get_array_size(2147483647), s8));   // 2147483647
    printf("-2147483648 : %s\n", convert_int_to_str(-2147483648, get_array_size(-2147483648), s9));   // -2147483648

    return 0;
}
```

### 実行結果
`get_array_size()`で`\0`用の1バイトを用意し`convert_int_to_str()`で`\0`を最後の要素に挿入したことで、想定結果どおりに出力されていることが分かります。
```
get_array_size():
        123 : 4
       -123 : 5
          9 : 2
         10 : 3
          1 : 2
          0 : 2
         -1 : 3
 2147483647 : 11
-2147483648 : 12

convert_int_to_str():
        123 : 123
       -123 : -123
          9 : 9
         10 : 10
          1 : 1
          0 : 0
         -1 : -1
 2147483647 : 2147483647
-2147483648 : -2147483648
```

## まとめ
この記事を通じて以下のようなことが学習出来ました。
- `printf()`は`\0`が来るまで文字列を出力すること
- 固定文字配列は`\0`用のバイトを用意して、ちゃんと最後の要素として挿入すること
- ちゃんとしてれば、文字化けは怖くない

## 参考
- [C言語で文字列を扱う時に起きたことなのですが - Yahoo!知恵袋](https://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q14122267069)