详解字符数字转换 atoi 与 strtol

来源:互联网 发布:归并排序java运用 编辑:程序博客网 时间:2024/05/17 04:09

众所周知,C++中的字符转换atoi 与 strtol很重要,但同时它也让很多人头痛不已,现在达内的老师们为大家提供一些总结,供大家参考,大家要仔细的记录下来啊。

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

     atoi   (把字符串转到整形)    --对应--   strtol  (把字符串转到长整形)                atol   (把字符串转到长整形)    --对应--   strtol  (把字符串转到长整形)                atof   (把字符串转到浮点数)    --对应--   strtod (把字符串转到浮点数) 

2. atoX 系列是 三十年前的函数 strtoX 系列是后十年产品

  1. 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,但是左边会置失败标志位。

  2. msvcr80.dll 的具体实现:

view source

print?
1 int __cdecl atoi(__in_z const char *_Str){ return _tstoi(_Str); }
2 int __cdecl _tstoi( const _TCHAR *nptr ){ return (int)_tstol(nptr);}
3 int __cdecl _tstoi( const _TCHAR *nptr ){ return (int)_tstol(nptr);}
4 long __cdecl _tstol(const _TCHAR *nptr){return _tcstol(nptr, NULL, 10);}
5 #define _tcstol strtol
view source

print?
01

extern "C" long __cdecl strtol ( 
02 const char *nptr,
03 char **endptr,
04 int ibase
05 )
06 {
07 if (__locale_changed == 0)
08 {
09 return (long) strtoxl(&__initiallocalestructinfo, nptr, (const char **)endptr, ibase, 0);
10 }
11 else
12 {
13 return (long) strtoxl(NULL, nptr, (const char **)endptr, ibase, 0);
14 }
15 }
16

view source

print?
001 static unsigned long __cdecl strtoxl (
002 _locale_t plocinfo,
003 const char *nptr,
004 const char **endptr,
005 int ibase,
006 int flags
007 )
008 {
009 const char *p;
010 char c;
011 unsigned long number;
012 unsigned digval;
013 unsigned long maxval;
014 _LocaleUpdate _loc_update(plocinfo);
015
016 /* validation section */
017 if (endptr != NULL)
018 {
019 /* store beginning of string in endptr */
020 endptr = (char )nptr;
021 }
022 _VALIDATE_RETURN(nptr != NULL, EINVAL, 0L);
023 _VALIDATE_RETURN(ibase == 0 || (2 <= ibase && ibase <= 36), EINVAL, 0L);
024
025 p = nptr; /* p is our scanning pointer */
026 number = 0; /* start with zero */
027 //1. 这里关注到,函数没有检查传入的原字符指针是否为空, 如果传递了一个空的就崩了….
028 c = p++; / read char */
029 while ( _isspace_l((int)(unsigned char)c, _loc_update.GetLocaleT()) )
030 c = p++; / skip whitespace */
031
032 //2. 不要期望能够 转换负负得正的字符串, 注意 “–100” 得到 0 , “-100” 得到 -100
033 if (c == ‘-‘) {
034 flags |= FL_NEG; /* remember minus sign */
035 c = *p++;
036 }
037 else if (c == ‘+’)
038 c = p++; / skip sign */
039 //3. 基数是 2 到 36 的闭区间 , [2, 36]
040 if (ibase < 0 || ibase == 1 || ibase > 36) {
041 /* bad base! */
042 if (endptr)
043 /* store beginning of string in endptr */
044 *endptr = nptr;
045 return 0L; /* return 0 */
046 }
047
048 //4. 如果转换的时候基数输入是0, 则基数取决于原字符的前面两个字符,
049 // 以非0开头的是 10进制字符串,
050 // 以0x或者0X开头的是 16进制字符串,
051 // 而仅仅以 0开头的是 8进制
052 else if (ibase == 0) {
053 /* determine base free-lance, based on first two chars of
054 string */
055 if (c != ‘0’)
056 ibase = 10;
057 else if (*p == ‘x’ || *p == ‘X’)
058 ibase = 16;
059 else
060 ibase = 8;
061 }
062
063 // {{{ 源码里面,这个地方 有这么一段 暂时不知道是干嘛的, 在我看来貌似是多余的
064 if (ibase == 0) {
065 /* determine base free-lance, based on first two chars of
066 string */
067 if (c != ‘0’)
068 ibase = 10;
069 else if (*p == ‘x’ || *p == ‘X’)
070 ibase = 16;
071 else
072 ibase = 8;
073 }
074 //}}}
075
076 // 5. 如果是 16 进制,则跳过0x 或者 0X 的前缀
077 if (ibase == 16) {
078 /* we might have 0x in front of number; remove if there */
079 if (c == ‘0’ && (*p == ‘x’ || *p == ‘X’)) {
080 ++p;
081 c = p++; / advance past prefix */
082 }
083 }
084
085 // 6. 下面就是读取字符串,然后按照 local 解析应用的数值, 如果在转换过程中出现各种情况都会对标志位flags 进行标记
086 /* if our number exceeds this, we will overflow on multiply */
087 maxval = ULONG_MAX / ibase;
088
089
090 for (;;) { /* exit in middle of loop */
091 /* convert c to value */
092 if ( __ascii_isdigit_l((int)(unsigned char)c, _loc_update.GetLocaleT()) )
093 digval = c - ‘0’;
094 else if ( __ascii_isalpha_l((int)(unsigned char)c, _loc_update.GetLocaleT()) )
095 digval = __ascii_toupper(c) - ‘A’ + 10;
096 else
097 break;
098 if (digval >= (unsigned)ibase)
099 break; /* exit loop if bad digit found */
100
101 /* record the fact we have read one digit */
102 flags |= FL_READDIGIT;
103
104 /* we now need to compute number = number * base + digval,
105 but we need to know if overflow occured. This requires
106 a tricky pre-check. */
107
108 if (number < maxval || (number == maxval &&
109 (unsigned long)digval <= ULONG_MAX % ibase)) {
110 /* we won’t overflow, go ahead and multiply */
111 number = number * ibase + digval;
112 }
113 else {
114 /* we would have overflowed – set the overflow flag */
115 flags |= FL_OVERFLOW;
116 if (endptr == NULL) {
117 /* no need to keep on parsing if we
118 don’t have to return the endptr. */
119 break;
120 }
121 }
122
123 c = p++; / read next digit */
124 }
125
126 –p; /* point to place that stopped scan */
127
128 if (!(flags & FL_READDIGIT)) {
129 /* no number there; return 0 and point to beginning of
130 string */
131 if (endptr)
132 /* store beginning of string in endptr later on */
133 p = nptr;
134 number = 0L; /* return 0 */
135 }
136 else if ( (flags & FL_OVERFLOW) ||
137 ( !(flags & FL_UNSIGNED) &&
138 ( ( (flags & FL_NEG) && (number > -LONG_MIN) ) ||
139 ( !(flags & FL_NEG) && (number > LONG_MAX) ) ) ) )
140 {
141 /* overflow or signed overflow occurred */
142 errno = ERANGE; //(老的实现方式和新的实 现方式区别主要在这里, 新版友记录转换过程)
143 if ( flags & FL_UNSIGNED )
144 number = ULONG_MAX;
145 else if ( flags & FL_NEG )
146 number = (unsigned long)(-LONG_MIN);
147 else
148 number = LONG_MAX;
149 }
150
151 if (endptr != NULL)
152 /* store pointer to char that stopped the scan */
153 *endptr = p;
154
155 if (flags & FL_NEG)
156 /* negate result if there was a neg sign */
157 number = (unsigned long)(-(long)number);
158
159 return number; /* done. */
160 }

所以 atoi 已经等同于strtol

0 0