前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >你可能没有实现一个正确的atoi函数

你可能没有实现一个正确的atoi函数

作者头像
编程珠玑
发布2019-09-02 22:19:33
2.3K0
发布2019-09-02 22:19:33
举报
文章被收录于专栏:编程珠玑编程珠玑

前言

我们都知道,atoi函数用于将一个字符串转换成整数。atoi函数看起来似乎很容易实现,你甚至可以很快写出一个版本,但是是否符合要求呢?

简易版本

最简单的考虑,就是遍历字符串,每遇到一个数字就加上原来的值乘以10。例如字符串“1234”转整数是这样的计算流程:

  • 遇到字符1,得到结果1;
  • 遇到字符2,得到结果1 * 10 + 2,即12;
  • 遇到字符3,得到结果12 * 10 + 3,即123;
  • 遇到字符4,得到结果123 * 10 + 4,即1234;

代码实现如下:

int my_atoi(const char *str)
{
    if(NULL == str)
        return 0;
    int ret = 0;
    while(0 != *str)
    {
        ret = ret * 10 + *str - '0';
        str++;
    }
    return ret;
}

看起来既简洁又没有什么问题,输入数值时也似乎能得到正确结果。真的是这样吗?如果传入以下字符串参数,会是什么结果呢?

"-1"
"+1"
"  "
"111111111111"
""
"1aab"

是不是发现并不是想象中的那样?那么实现atoi到底需要注意什么呢?

实现atoi函数需要注意什么

你可能已经注意到了,实现atoi需要考虑下面这些场景:

  • 输入正负号
  • 开头有空格
  • 转换后的数值超出int的表示范围
  • 出错时返回0与正确转换0的区别
  • 输入非数字
  • 空字符串

现在来看,前面的实现还能满足要求吗?

再次实现

那么重新考虑上面的要求,我们如何实现呢?我们需要考虑以下几种情况

  • 如果开头是负号,则标记为负数;正号或数值,则标记为正数
  • 跳过开头的空格,从第一个有效字符开始
  • 使用更大类型存储计算值,如果负数比INT_MIN还小或正数比INT_MAX还大,则表明溢出,返回INT_MIN或INT_MAX,或者在下次计算之前与INT_MIN/10或INT_MAX/10比较
  • 使用全局变量记录出错情况,区别正常转换为0或最大最小值
  • 遇到非数值时即退出

根据上面这些考虑,我们重新实现代码:

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <limits.h>
#define NULL_PTR_ERR 1
#define ERANGE 2
int errCode = 0;
int my_atoi(char* str) {
    int negative = 0;
    char c = 0;
    /*计算结果存储*/
    long long ret = 0;
    errCode = 0;
    if(NULL == str)
    {
        errCode = NULL_PTR_ERR;
        return 0;
    }
    /*跳过开始的空格*/
    while(' ' == (*str))
        str++;
    /*跳过空格之后,到达了字符串结尾,则退出*/
    if(0 == *str)
        return 0;
    /*负数*/
    if(*str == '-' )
    {
        negative = 1;
        str++;
    }
    /*正数*/
    else if(*str == '+')
    {
        negative = 0;
        str++;
    }
    /*正数*/
    else if(isdigit(*str))
    {
        negative = 0;
    }
    /*如果不是以上内容,则直接退出*/
    else
    {
        return 0;
    }
    while(isdigit(*str))
    {
        /*计算结果*/
        ret = ret*10 + *str -'0';

        /*如果发现结果大于INT_MAX或者小于INT_MIN,则溢出,返回最值*/
        if(ret > (negative?-(long long )INT_MIN:INT_MAX))
        {
            /*溢出,返回最大值*/
            errCode = ERANGE;
            return negative?INT_MIN:INT_MAX;
        }
        str++;
    }
    /*根据正负号返回正确的结果*/
    return negative?-ret:ret;
}
int main(void)
{
    /*只有一个负号*/
    int result = my_atoi("-");
    printf("-:%d,errCode:%d\n",result,errCode);

    /*空指针*/
    result = my_atoi(NULL);
    printf("NULL:%d,errCode:%d\n",result,errCode);
    /*空字符串*/
    result = my_atoi("      ");
    printf("     :%d,errCode:%d\n",result,errCode);

    /*负数*/
    result = my_atoi("-1");
    printf("  -1:%d,errCode:%d\n",result,errCode);

    /*负数溢出*/
    result = my_atoi("   -11111111111");
    printf("   -11111111111:%d,errCode:%d\n",result,errCode);

    /*正数*/
    result = my_atoi("+123");
    printf("+123:%d,errCode:%d\n",result,errCode);

     /*正数溢出*/
    result = my_atoi("+123111111111111111");
    printf("+123111111111111111:%d,errCode:%d\n",result,errCode);
    return 0;
}

运行结果:

-:0,errCode:0
NULL:0,errCode:1
     :0,errCode:0
  -1:-1,errCode:0
   -11111111111:-2147483648,errCode:2
+123:123,errCode:0
+123111111111111111:2147483647,errCode:2

总结

上面的代码中errCode的设置需要根据需求而定,例如如果认为空字符串或只有负号转换是非法的,那么前面的代码将不符合要求。但这些都不是重点,重点是我们在考虑实现atoi函数的时候,需要考虑多种异常场景,这在平常实现其他功能接口的时候也是一样的。

思考

前面的代码有什么不足?你忽略了哪些场景?

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-03-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 编程珠玑 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 简易版本
  • 实现atoi函数需要注意什么
  • 再次实现
  • 总结
  • 思考
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档