今天,在review 一些代码的时候,看到了strtol()这个函数,由于以前使用它的时候,还没有深刻的了解,这次,我决定探个究竟。

网上关于这个函数的资料大都来源于同份资料,linux库函数,讲的不够细致。于是,我花了几个小时,认真地尝试其功能,并整理了这篇文章,希望能对C语言的爱好者一些帮助。

希望大家能够将本文中发现的错误及时反馈给我,以便修正。我的信箱是​com">​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>头文件中。