字符数字转换 atoi 与 strtol

Posted 智者无疆

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了字符数字转换 atoi 与 strtol相关的知识,希望对你有一定的参考价值。

原文:http://www.cnblogs.com/JefferyZhou/archive/2010/07/01/1769555.html

 

在很多时候我们都很清楚 atoX 系列函数: atoi , atol , atof
新来的一系列函数:  strtol,  strtoul, strtod 
通常有如下的关系:
1. 对应关系其中: 

          atoi   (把字符串转到整形)    --对应--   strtol  (把字符串转到长整形)           

          atol   (把字符串转到长整形)    --对应--   strtol  (把字符串转到长整形)           

          atof   (把字符串转到浮点数)    --对应--   strtod (把字符串转到浮点数) 
2. atoX 系列是 三十年前的函数 strtoX 系列是后十年产品


3. atoX 系列接口,没有成功失败的区别(标准实现中),    strtoX 系列接口,有成功失败的区别


    比如:int i_atoi_lfs =  atoi(""); 与 int i_atoi_rfs = atoi("0"); 两个得到的是一样的,没有任何区别         

     而: int i_atoi_lfs =  strtol  ("", NULL,10); 与 int i_atoi_rfs = strtol  ("0", NULL,10);  得到的结果都是0,但是左边会置失败标志位。


4. msvcr80.dll  的具体实现:

 

1
2
3
4
5
int __cdecl atoi(__in_z const char *_Str){  return _tstoi(_Str);  }   
int __cdecl _tstoi(     const _TCHAR *nptr        ){    return (int)_tstol(nptr);}
int __cdecl _tstoi(     const _TCHAR *nptr        ){    return (int)_tstol(nptr);}
long __cdecl _tstol(const _TCHAR *nptr){return _tcstol(nptr, NULL, 10);}
#define _tcstol   strtol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<pre class="brush:cpp">extern "C" long __cdecl strtol (
        const char *nptr,
        char **endptr,
        int ibase
        )
{
    if (__locale_changed == 0)
    {
        return (long) strtoxl(&__initiallocalestructinfo, nptr, (const char **)endptr, ibase, 0);
    }
    else
    {
        return (long) strtoxl(NULL, nptr, (const char **)endptr, ibase, 0);
    }
}
</pre>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
static unsigned long __cdecl strtoxl (
        _locale_t plocinfo,
        const char *nptr,
        const char **endptr,
        int ibase,
        int flags
        )
{
        const char *p;
        char c;
        unsigned long number;
        unsigned digval;
        unsigned long maxval;
        _LocaleUpdate _loc_update(plocinfo);
 
        /* validation section */
        if (endptr != NULL)
        {
            /* store beginning of string in endptr */
            *endptr = (char *)nptr;
        }
        _VALIDATE_RETURN(nptr != NULL, EINVAL, 0L);
        _VALIDATE_RETURN(ibase == 0 || (2 <= ibase && ibase <= 36), EINVAL, 0L);
 
        p = nptr;                       /* p is our scanning pointer */
        number = 0;                     /* start with zero */
//1. 这里关注到,函数没有检查传入的原字符指针是否为空, 如果传递了一个空的就崩了....
        c = *p++;                       /* read char */
        while ( _isspace_l((int)(unsigned char)c, _loc_update.GetLocaleT()) )
            c = *p++;               /* skip whitespace */
 
//2. 不要期望能够 转换负负得正的字符串, 注意 "--100" 得到 0 ,  "-100" 得到 -100
        if (c == \'-\') {
            flags |= FL_NEG;        /* remember minus sign */
            c = *p++;
        }
        else if (c == \'+\')
            c = *p++;               /* skip sign */
//3.  基数是 2 到 36 的闭区间 , [2, 36]
        if (ibase < 0 || ibase == 1 || ibase > 36) {
            /* bad base! */
            if (endptr)
                /* store beginning of string in endptr */
                *endptr = nptr;
            return 0L;              /* return 0 */
        }
 
//4. 如果转换的时候基数输入是0, 则基数取决于原字符的前面两个字符,
// 以非0开头的是 10进制字符串,
// 以0x或者0X开头的是 16进制字符串,
// 而仅仅以 0开头的是 8进制
        else if (ibase == 0) {
            /* determine base free-lance, based on first two chars of
               string */
            if (c != \'0\')
                ibase = 10;
            else if (*p == \'x\' || *p == \'X\')
                ibase = 16;
            else
                ibase = 8;
        }
 
//  {{{   源码里面,这个地方 有这么一段 暂时不知道是干嘛的, 在我看来貌似是多余的
        if (ibase == 0) {
            /* determine base free-lance, based on first two chars of
               string */
            if (c != \'0\')
                ibase = 10;
            else if (*p == \'x\' || *p == \'X\')
                ibase = 16;
            else
                ibase = 8;
        }
//}}}
 
// 5. 如果是 16 进制,则跳过0x 或者 0X 的前缀
        if (ibase == 16) {
            /* we might have 0x in front of number; remove if there */
            if (c == \'0\' && (*p == \'x\' || *p == \'X\')) {
                ++p;
                c = *p++;       /* advance past prefix */
            }
        }
 
// 6. 下面就是读取字符串,然后按照 local 解析应用的数值, 如果在转换过程中出现各种情况都会对标志位flags 进行标记
        /* if our number exceeds this, we will overflow on multiply */
        maxval = ULONG_MAX / ibase;
 
 
        for (;;) {      /* exit in middle of loop */
            /* convert c to value */
            if ( __ascii_isdigit_l((int)(unsigned char)c, _loc_update.GetLocaleT()) )
                digval = c - \'0\';
            else if ( __ascii_isalpha_l((int)(unsigned char)c, _loc_update.GetLocaleT()) )
                digval = __ascii_toupper(c) - \'A\' + 10;
            else
                break;
            if (digval >= (unsigned)ibase)
                break;          /* exit loop if bad digit found */
 
            /* record the fact we have read one digit */
            flags |= FL_READDIGIT;
 
            /* we now need to compute number = number * base + digval,
               but we need to know if overflow occured.  This requires
               a tricky pre-check. */
 
            if (number < maxval || (number == maxval &&
                        (unsigned long)digval <= ULONG_MAX % ibase)) {
                /* we won\'t overflow, go ahead and multiply */
                number = number * ibase + digval;
            }
            else {
                /* we would have overflowed -- set the overflow flag */
                flags |= FL_OVERFLOW;
                if (endptr == NULL) {
                    /* no need to keep on parsing if we
                       don\'t have to return the endptr. */
                    break;
                }
            }
 
            c = *p++;               /* read next digit */
        }
 
        --p;                            /* point to place that stopped scan */
 
        if (!(flags & FL_READDIGIT)) {
            /* no number there; return 0 and point to beginning of
               string */
            if (endptr)
                /* store beginning of string in endptr later on */
                p = nptr;
            number = 0L;            /* return 0 */
        }
        else if ( (flags & FL_OVERFLOW) ||
                ( !(flags & FL_UNSIGNED) &&
                  ( ( (flags & FL_NEG) && (number > -LONG_MIN) ) ||
                    ( !(flags & FL_NEG) && (number > LONG_MAX) ) ) ) )
        {
            /* overflow or signed overflow occurred */
            errno = ERANGE;                  //(老的实现方式和新的实现方式区别主要在这里, 新版友记录转换过程)
            if ( flags & FL_UNSIGNED )
                number = ULONG_MAX;
            else if ( flags & FL_NEG )
                number = (unsigned long)(-LONG_MIN);
            else
                number = LONG_MAX;
        }
 
        if (endptr != NULL)
            /* store pointer to char that stopped the scan */
            *endptr = p;
 
        if (flags & FL_NEG)
            /* negate result if there was a neg sign */
            number = (unsigned long)(-(long)number);
 
        return number;                  /* done. */
}

 

 

所以 atoi 已经等同于strtol 

Sign,  Clown , 2010.07.01 . 23:32 . HDPY

[本文原创,转载请注明出处,在文章末尾提供原文链接http://www.cnblogs.com/JefferyZhou/,否则一旦发现,将按字节每人民币收费,绝不论价]

以上是关于字符数字转换 atoi 与 strtol的主要内容,如果未能解决你的问题,请参考以下文章

[C/C++笔面试]不使用库函数将字符串转换为整数

C语言中字符串和整数小数相互转换的函数以及头文件

atoi() 来自十六进制表示字符串

8. 字符串转换整数 (atoi)

十六进制ascii码转浮点数

c语言,用循环的方法将字符串“123”转化成数字123,应该怎样写?