今天,在review 一些代码的时候,看到了strtol()这个函数,由于以前使用它的时候,还没有深刻的了解,这次,我决定探个究竟。
网上关于这个函数的资料大都来源于同份资料,linux库函数,讲的不够细致。于是,我花了几个小时,认真地尝试其功能,并整理了这篇文章,希望能对C语言的爱好者一些帮助。
希望大家能够将本文中发现的错误及时反馈给我,以便修正。我的信箱是wp.qin@sunmedia.com 。
+----------------+
| strtol |
+----------------+
i.e. string to long
long int strtol(const char *nptr, char **endptr, int base)
strtol()会将nptr指向的字符串,根据参数base,按权转化为long int, 然后返回这个值。
参数base的范围为2~36,和0;它决定了字符串以被转换为整数的权值。
可以被转换的合法字符依据base而定,举例来说,当base为2时,合法字符为‘0’,‘1’;base为8时,合法字符为‘0’,‘1’,……‘7’;base为10时,合
法字符为‘0’,‘1’,……‘9’;base 为16时,合法字符为‘0’,‘1’,……‘9’,‘a’,……‘f’;base为24时,合法字符为‘0’,……‘9’,‘a’,……‘n’,base
为36时,合法字符为‘0’,……‘9’,‘a’,……‘z’;等等。其中,不区分大小写,比如,‘A’和‘a’会都会被转化为10。
当字符合法时,‘0’,……‘9’依次被转换为十进制的0~9,‘a’,……‘z’一次北转换为十进制的10~35。
strtol()函数检测到第一个非法字符时,立即停止检测,其后的所有字符都会被当作非法字符处理。合法字符串会被转换为long int, 作为函数的返
回值。非法字符串,即从第一个非法字符的地址,被赋给*endptr。**endptr是个双重指针,即指针的指针。strtol()函数就是通过它改变*endptr的值,即把第一个非法字符的地址传给endptr。
多数情况下,endptr设置为NULL, 即不返回非法字符串。
下面看几个例子:
------------------------------------------------------
char buffer[20]="10379cend$3";
char *stop;
printf("%d/n",strtol(buffer, &stop, 2));
printf("%s/n", stop);
输出结果:
2
379cend$3
-------------------------------------------------------
char buffer[20]="10379cend$3";
char *stop;
printf("%d/n",strtol(buffer, &stop, 8));
printf("%s/n", stop);
输出结果:
543
9cend$3
--------------------------------------------------------
char buffer[20]="10379cend$3";
char *stop;
printf("%d/n",strtol(buffer, &stop, 10));
printf("%s/n", stop);
输出结果:
10379
cend$3
-------------------------------------------------------
char buffer[20]="10379cend$3";
char *stop;
printf("%d/n",strtol(buffer, &stop, 16));
printf("%s/n", stop);
输出结果:
17005006
nd$3
另外,如果base为0,且字符串不是以0x(或者0X)开头,则按十进制进行转化。如果base为0或者16,并且字符串以0x(或者0X)开头,那么,x(
或者X)被忽略,字符串按16进制转化。如果base不等于0和16,并且字符串以0x(或者0X)开头,那么x被视为非法字符。
例如:
-------------------------------------------------------
char buffer[20]="0x31da6c";
char *stop;
printf("%d/n",strtol(buffer, &stop, 0));
printf("%s/n", stop);
输出结果(stop为空):
3267180
-------------------------------------------------------
char buffer[20]="0x31da6c";
char *stop;
printf("%d/n",strtol(buffer, &stop, 13));
printf("%s/n", stop);
输出结果:
0
0x31da6c
-------------------------------------------------------
最后,需要说明的是,对于nptr指向的字符串,其开头和结尾处的空格被忽视,字符串中间的空格被视为非法字符。
例如:
-------------------------------------------------------
char buffer_1[20]="10379c";
char buffer_2[20]=" 10379c ";
char buffer_3[20]=" 10 379c ";
printf("%d/n",strtol(buffer_1,NULL,0));
printf("%d/n",strtol(buffer_2,NULL,0));
printf("%d/n",strtol(buffer_3,NULL,0));
输出结果为:
10379
10379
10
-----------------------------------------------------------
---------------------------------------- 分割线 ---------------------------------------------------
上面是转载的内容
下面是自己的一些理解:
1.对于strtol的返回值问题
RETURN VALUE
The strtol() function returns the result of the conversion, unless the value would
underflow or overflow. If an underflow occurs, strtol() returns LONG_MIN. If an
overflow occurs, strtol() returns LONG_MAX. In both cases, errno is set to ERANGE.
Precisely the same holds for strtoll() (with LLONG_MIN and LLONG_MAX instead of
LONG_MIN and LONG_MAX).
在man手册中有这样一段返回值的介绍,这说明strtol这个函数的出错只有两种,第一是上溢,第二是下溢,两种分别对应于不同的返回值LONG_MAX和LONG_MIN。所以在用这个函数的时候一定要判断转出的数值不能溢出。
2.关于strtol中endptr的问题
错误的理解:
static int hex2bin(char *hex_num) { char *tmp; char bin_arr[16] = {0}; int bin, i; char *endptr = NULL; STF_debug_print("Hex number is %s/n", hex_num); /* tmp = hex_num + 2 -> cut off '0x'*/ for(tmp = hex_num + 2, i = 0; *tmp != '/0'; tmp++, i+=4){ switch(*tmp) { case '0': strcpy(&bin_arr[i], "0000"); break; case '1': strcpy(&bin_arr[i], "0001"); break; case '2': strcpy(&bin_arr[i], "0010"); break; case '3': strcpy(&bin_arr[i], "0011"); break; case '4': strcpy(&bin_arr[i], "0100"); break; case '5': strcpy(&bin_arr[i], "0101"); break; case '6': strcpy(&bin_arr[i], "0110"); break; case '7': strcpy(&bin_arr[i], "0111"); break; case '8': strcpy(&bin_arr[i], "1000"); break; case '9': strcpy(&bin_arr[i], "1001"); break; case 'a': case 'A': strcpy(&bin_arr[i], "1010"); break; case 'b': case 'B': strcpy(&bin_arr[i], "1011"); break; case 'c': case 'C': strcpy(&bin_arr[i], "1100"); break; case 'd': case 'D': strcpy(&bin_arr[i], "1101"); break; case 'e': case 'E': strcpy(&bin_arr[i], "1110"); break; case 'f': case 'F': strcpy(&bin_arr[i], "1111"); break; default: STF_debug_print("Error hex number/n"); break; } } STF_debug_print("bin_arr is %s/n", bin_arr); bin = strtol(bin_arr, &endptr, 2); #if 0 if(endptr != NULL){ STF_debug_print("strtol error, and endptr = %s/n", endptr == ' '? "space":endptr); return -1; } #else if(*endptr != '/0'){ STF_debug_print("strtol error, and endptr = %s/n", endptr == ' '? "space":endptr); }else{ STF_debug_print("endptr = %s/n", endptr == NULL ? "null" : (endptr == '/0') ? "00000" : endptr); } #endif return
上面是一段十六进制字符串转二进制字符串的代码,在函数结尾,我本来想用endptr来判断strtol的转换是否正确,当时的想法是:如果一个纯以'0'或'1'字符构成的字符串,那么正确转换后endptr应该是NULL。
试验结果:
int main() { char str1[16] = "adafadfd"; char *endptr; int num; num = strtol(str1, &endptr, 16); if(num == LONG_MAX || num == LONG_MIN){ printf("Num overflow!"); printf(", and endptr is %s/n", *endptr == '/0' ? "0" : endptr); }else{ printf("str is %s, and strtol is %d",str1, num); printf(", and endptr is %s/n", *endptr == '/0' ? "0" : endptr); } char str2[16] = "1111111"; printf("str is %s, and strtol is %d",str2, strtol(str2, &endptr, 2)); printf(", and endptr is %s/n", *endptr == '/0' ? "0" : endptr); char str3[16] = "aaaaasss"; printf("str is %s, and strtol is %d",str3, strtol(str3, &endptr, 16)); printf(", and endptr is %s/n", *endptr == '/0' ? "0" : endptr); return 0; }
执行结果:
Num overflow!, and endptr is 0 str is 1111111, and strtol is 127, and endptr is 0 str is aaaaasss, and strtol is 699050, and endptr is sss
经过上述代码的一个测试,可以发现:
1. 当字符串中的字符完全符合转换所规定进制的格式要求时,*ptr = '/0',无论是否溢出(情况1和2)
2. 当字符串中出现非法字符,那么*ptr = 第一个非法字符。
3. LONG_MAX和LONG_MIN 常数在<limit.h>头文件中。