はじめに
- 技術的な質問やコメント等をくださると大変うれしいです
- 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型の数値を固定文字配列に挿入するというコードになります。
#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
- 配列変数
#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
用のバイトを用意して、ちゃんと最後の要素として挿入すること - ちゃんとしてれば、文字化けは怖くない