<leetcode系列> String to Integer (atoi) 以及atoi源码实现

来源:互联网 发布:零基础学c语言视频 编辑:程序博客网 时间:2024/09/21 09:29

String to Integer (atoi)

Implement atoi to convert a string to an integer.

Hint: Carefully consider all possible input cases. If you want a challenge, please do not see below and ask yourself what are the possible input cases.

Notes: It is intended for this problem to be specified vaguely (ie, no given input specs). You are responsible to gather all the input requirements up front.

原题链接: https://leetcode.com/problems/string-to-integer-atoi/

题目大意: 类似于实现一个自己的atoi函数.

编译调试了很久, 遇到了不少感觉怪异的测试用例.列举如下:

测试用例 期望值 “+-2” 0 “10522545459” 2147483647 ” 105225454” 105225454 “-2247483647” -2147483648 ” 123adf” 123 “ad12” 0 ” -123 5a43f” -123

还有一些没有列举出来. 大部分都是对数据溢出的处理问题.当数据溢出时, 正数的期望值是INT_MAX, 负数的期望值是INT_MIN.以及字符串开头的空格,符号位等.

其中数据溢出有两种情况.
1. 在已解析出的数值为214748364时, 而下一位的数值是8. 而int的最大值是2147483647.则数据溢出
2. 在已解析出的数值为314748364时, 下一位无论是任何数, 均会数据溢出.

最终提交代码如下:

int myAtoi(char* str) {    if (NULL == str) {        return 0;    }    const int MAX_SIZE = 10;    char* pStr = str;    char* pSta = str;// 用于前置空格的排除    bool sig = false; // false -- +; true -- -    int num  = 0;    int tmp  = 0;    int count = 0; // 记录当前数值长度    while ('\0' != *pStr) {        if ((*pStr >= '0') && (*pStr <= '9')) {            if (MAX_SIZE == count) { // 数据溢出                return sig ? INT_MIN : INT_MAX;            }            int tmp = *pStr - '0';            // 正负数分别计算            num = sig ? (10 * num - tmp) : (10 * num + tmp);            ++count;        } else {            if (pStr == pSta) {                switch (*pStr) {                case '-':                    sig = true;                    break;                case '+':                    sig = false;                    break;                case ' ':                    ++pSta;                    break;                default:                    return 0;                }            } else {                break;            }        }        ++pStr;    }    // 数据溢出    if (sig && (num > 0)) {        return INT_MIN;    } else if (!sig && (num < 0)) {        return INT_MAX;    }    return num;}

显然, 在上述代码中, 肯定存在不少缺陷并且不够精炼.整个函数看起来繁琐, 各种标志位.效率当然也会大打折扣.

相信atoi函数对于大部分C/C++程序员都是一个比较熟悉的函数.在网站上查询了一下, atoi源代码确实写得很漂亮, 和我的代码比较起来, 简直一个天上一个地下啊.

可以看到atoi库函数的源码实现如下,供大家参阅:

int atoi(const char *nptr){    return (int) strtol(nptr, (char **) NULL, 10);}long __XL(strtol)(const char * __restrict str, char ** __restrict endptr,  int base   __LOCALE_PARAM ){    return __XL_NPP(_stdlib_strto_l)(str, endptr, base, 1   __LOCALE_ARG );}// 下述函数才是真正完成任务的函数// 实际调用时, str 是输入的nptr, endptr 是 NULL, base 是 10, sflag 是 1// 即 unsigned long  _XL_NPP(_stdlib_strto_l)(nptr, NULL, 10, 1)unsigned long __XL_NPP(_stdlib_strto_l)(register const Wchar * __restrict str,Wchar ** __restrict endptr, int base,int sflag   __LOCALE_PARAM ){    unsigned long number, cutoff;#if _STRTO_ENDPTR    const Wchar *fail_char;#define SET_FAIL(X)       fail_char = (X)#else#define SET_FAIL(X)       ((void)(X)) /* Keep side effects. */#endif    unsigned char negative, digit, cutoff_digit;    assert(((unsigned int)sflag) <= 1);    SET_FAIL(str);    while (ISSPACE(*str)) { /* 忽略str中开头的空格 */        ++str;    }    /* 0 代表 正数; 1 代表 负数 */    negative = 0;    switch(*str) {    /* 注意到没有break, 无论*str是'+'或是'-',str均会自加 */    case '-': negative = 1;     case '+': ++str;    }    /* 上述操作结束后, 将str前面的空格或是符号位均已跳过.       只跳过一个符号位,若出现"+-2"的情况, 那么当前*str是'-' */    if (!(base & ~0x10)) { /* 0x10的十进制是16, 当base等于16时, 该条件为真;否则为假;    */    // 当然,还有可能有其他情况下会进入.比如base=0x******10(*代表任意数);        base += 10; /* default is 10(26). */        if (*str == '0') {            SET_FAIL(++str);            base -= 2;  /* Now base is 8 or 16 (24). */            if ((0x20|(*str)) == 'x') { /* WARNING: assumes ascii. */                ++str;                base += base;   /* Base is 16 (16 or 48). */            }        }        if (base > 16) {    /* Adjust in case base wasn't dynamic. */            base = 16;        }    }    number = 0;    if (((unsigned)(base - 2)) < 35) { /* 最大能计算的进制是36进制. */        cutoff_digit = ULONG_MAX % base; // 计算当数值等于临界值时最大还能加上多大的数        cutoff = ULONG_MAX / base; // 计算判断数据溢出的临界值        do {            // 判断每一位的数值; 代码执行类似如下            /* if (((Wuchar)(*str - '0')) <= 9) {                digit = *str - '0';            } else {                if (*str >= 'A') {                    // (0x20|(*str)) 将*str的第6位置1.                    // 即将所有大写字母的ascii码变为小写字母的ascii码, 而小写字母则不变                    // 不清楚的同学可以百度ascii码表, 字母的大小写ascii码只是在二进制的第六位不同.                    // 大写字母的二进制第六位是0, 而小写的二进制第六位是1                    digit = (((0x20|(*str)) - 'a' + 10));                } else {                    digit = 40; // 该位是非法字符.                }            } */            digit = (((Wuchar)(*str - '0')) <= 9)                ? (*str - '0')                : ((*str >= 'A')                   ? (((0x20|(*str)) - 'a' + 10)) /* WARNING: assumes ascii. */                  : 40);            // 遇到非法字符, 如16进制, 遇到了'?', 10进制,遇到了'A'            if (digit >= base) {                break;            }            SET_FAIL(++str);            // 判断加上该位的数值后数据溢出            if ((number > cutoff)                || ((number == cutoff) && (digit > cutoff_digit))) {                number = ULONG_MAX;                negative &= sflag;                SET_ERRNO(ERANGE);            } else { // 未溢出                number = number * base + digit;            }        } while (1);    }#if _STRTO_ENDPTR    if (endptr) {        *endptr = (Wchar *) fail_char;    }#endif    {        // ((unsigned long)(-(1+LONG_MIN))) == LONG_MAX, 但不知道为什么要这样获取long的最大值.        // 当欲计算的数为负数时, tmp = LONG_MAX + 1;为正数时, tmp = LONG_MAX        unsigned long tmp = ((negative)             ? ((unsigned long)(-(1+LONG_MIN)))+1             : LONG_MAX);        if (sflag && (number > tmp)) {            number = tmp;            SET_ERRNO(ERANGE);        }    }    return negative ? (unsigned long)(-((long)number)) : number;}
0 0
原创粉丝点击