ゼロからシリーズ
この記事はゼロからcのライブラリを再実装するシリーズです。
atoi
の動作概要
atoi は char *
型の値を int
型の値へ変換するものです。
詳しい動作
-
appleの
atoi
を参照するとstrtol
のlong
の値をint
をキャストしているようです。 -
appleの
strtol
を参照すると詳しいことが見えてきそうです。- はじめのcharポインタから
isspace(3)
に準拠したスペースはすべて無視する仕様のようです。-
isspace(3)
->\t
,\n
,\v
,\f
,\r
-
- はじめの空白を除いたcharポインタは
-
か+
を1バイトだけ許容する仕様のようです。 - 上記のcharポインタの次に複数の数字を
0
~9
を許容するようで、数字でないものがcharポインタに入っているとそこで処理を抜ける仕様のようです。 -
char *
型の値をlong
型の値へ変換する際にlongをオーバーフローしたらLONG_MAX
かLONG_MIN
をreturnするようです。
- はじめのcharポインタから
atoi
実装
isspace
実装
int ft_isspace(char c)
{
if (c == ' ' || c == '\t' || c == '\n'
|| c == '\v' || c == '\f' || c == '\r')
return (1);
return (0);
}
long
のオーバーフロー検知を実装
int overflowl(int sign, long l, char next)
{
if (sign == 1)
if (LONG_MAX / 10 < l
|| (LONG_MAX / 10 == l && LONG_MAX % 10 <= next - '0'))
return (1);
if (sign == -1)
if (LONG_MIN / -10 < l
|| (LONG_MIN / -10 == l && LONG_MIN % -10 * -1 <= next - '0'))
return (-1);
return (0);
}
atoi
を実装
#include <limits.h>
int ft_atoi(const char *str)
{
long ret;
int sign;
ret = 0;
sign = 1;
while (ft_isspace(*str))
str++;
if (*str == '-' || *str == '+')
if (*str++ == '-')
sign = -1;
while (*str)
{
if ('0' <= *str && *str <= '9')
{
if (overflowl(sign, ret, *str) == 1)
return ((int)LONG_MAX);
if (overflowl(sign, ret, *str) == -1)
return ((int)LONG_MIN);
ret = ret * 10 + (long)(*str - '0');
str++;
}
else
break ;
}
return ((int)(ret * sign));
}
まとめ
今回はatoiをゼロから再実装しました。
All in one code
#include <limits.h>
int ft_isspace(char c)
{
if (c == ' ' || c == '\t' || c == '\n'
|| c == '\v' || c == '\f' || c == '\r')
return (1);
return (0);
}
int overflowl(int sign, long l, char next)
{
if (sign == 1)
if (LONG_MAX / 10 < l
|| (LONG_MAX / 10 == l && LONG_MAX % 10 <= next - '0'))
return (1);
if (sign == -1)
if (LONG_MIN / -10 < l
|| (LONG_MIN / -10 == l && LONG_MIN % -10 * -1 <= next - '0'))
return (-1);
return (0);
}
int ft_atoi(const char *str)
{
long ret;
int sign;
ret = 0;
sign = 1;
while (ft_isspace(*str))
str++;
if (*str == '-' || *str == '+')
if (*str++ == '-')
sign = -1;
while (*str)
{
if ('0' <= *str && *str <= '9')
{
if (overflowl(sign, ret, *str) == 1)
return ((int)LONG_MAX);
if (overflowl(sign, ret, *str) == -1)
return ((int)LONG_MIN);
ret = ret * 10 + (long)(*str - '0');
str++;
}
else
break ;
}
return ((int)(ret * sign));
}