一个健壮性良好的atoi函数的实现
1 函数说明
函数原型
intatoi(const char *nptr);
nptr:指向待转换的字符串的指针
返回值
字串的整型形式, 必须以NULL结尾.
说明
atoi函数跳过字串开头的所有空白字符, 转换接下来的数字字符, 遇到第一个非数字字符停止
C11的标准中关于atoi的描述为:
当遇到错误时, atoi不需要改变errno的值, 当值的结果无法表示时, 行为是未定义的.
除了错误处理外, 它等价于(equivalent): (int)strtol(nptr, (char**)NULL, 10)
2 函数黑盒测试
根据函数说明,确定边界条件,编写测试用例如下:
从以上数据, 可以分析出以下几点:
(1) 空字串("")或非法字串(如"abc"), 输出0;
(2) 可以有符号(如"123"),也可以没有(如"-123"), 如果有符号, 则符号后面必须是数字, 符号与数字之间不能有空格;
(3) 开头的空格将被过滤, 末尾的空格也不会管;
(4) 数字前面的字符'0'将被过滤(如"010");
(5) 如果超过最大值(如"2147483648"),则输出最大值2147483647(32位的最大int值); 如果超过最小值 (如"-2147483649"),则输出最小值-2147483648(32位的最小int值);
(6) 不只是空格, 开头和末尾的所有空白字符(isspace)都将被过滤;
(7) 开头的字符'0'并不会被当作空白字符那样被过滤. 可以想像到, 读到任意数字(如这里的'0')之后的 非数字(如这里的'+'), 都将停止。
3 自己编写atoi需要注意的问题:
(1) 字符串前的空白
(2) 字符串所表示数值的正负号
(3) 结束条件,遇到非数字或者字符'\0'结束
(4) 考虑溢出,分别与int值所能表示的最大(0x7fffffff)和最小值(0x8000000)进行比较。而且最好考虑 32位机与64位机的区别。
(5) 考虑异常输入情况下,用全局变量valid来标识,对于"+/-" "0" "+abc"需要进行区分。
4 自己实现atoi函数
//Nov.9,2014 Solomon int myatoi(const char* str) { int n = 0; char sign; int c; while (isspace(*str)) ++str; sign = *str; if (sign == '+' || sign == '-') ++str; while (isdigit(*str)) { c = *str - '0'; //先用n与MAX/10进行比较: 若n>MAX/10(还要考虑n=MAX/10的情况), 说明将要溢出了 //提高溢出处理的健壮性,除法代替乘法 if (sign != '-' && (n > INT_MAX/10 || (n == INT_MAX/10 && c >= INT_MAX%10))) { return INT_MAX; } else if (sign == '-' && (n > (unsigned)INT_MIN/10 || (n == (unsigned)INT_MIN/10 && c >= (unsigned)INT_MIN%10))) { return INT_MIN; } n = n * 10 + c; ++str; } return sign == '-' ? -n : n; }
5 测试函数
int main(){ int N,i; char *str[STR_ELEM_NUM]; memset(str,0,sizeof(str)); printf("测试数据个数N:\n"); scanf("%d",&N); for(i=0;i<N;i++){ str[i] = (char*) malloc(STR_ELEM_NUM); // 如果不清缓冲区,scanf会把"\n"吃进,导致str[0] = "",少输入一个数据. fflush(stdin); // str[i]是一个字符型指针,定义时指向不可用的地址,需要字符指针配内存空间 scanf("%[^\n]",str[i]); fflush(stdin); } printf("\n***************Result**************************\n"); for(i =0;i<N;i++){ printf("%d\n",myatoi(str[i])); free(str[i]); } return 0; }
测试结果如下:
结论:
(1) 该atoi函数符合C11规定的所有内容。
(2) 程序采用除法代替加法的方式提高处理溢出的健壮性。
(3) 该算法逻辑清晰,鲁棒性强,是一个较优的atoi实现。