[英]Why strtoul doesn't work as expected?
嗨,我编写了一个小型测试程序,以检查编写的函数如何将字符串(十六进制数)转换为无符号整数,并且我发现代码的行为取决于我使用的编译器或系统。
我在下面编译了以下代码:
(1)ideone C ++ 4.3.2 https://ideone.com/LlcNWw
(2)在centos6(64位)上的g ++ 4.4.7
(3)ubuntu12上的g ++ 4.6.3(64位)
(4)在Cygwin(32位)环境中的g ++ 4.9.3
如预期的那样(1)和(4)返回AND它是正确的结果,因为第一个值'0x210000000'对于32位值来说很大。
Error while converting Id (0x210000000).
success
但是(2)和(3)返回
success
success
所以问题是为什么在不同平台上使用不同的编译器来构建相同的简单C代码会返回相同的结果...以及为什么'strtoul(“ 0x210000000”,....)'不会将'errno'设置为'ERANGE'表示位33到37超出范围。
在平台上的更多跟踪信息(3)给出:
Id (0x210000000) as ul = 0x10000000 - str_end - errno 0.
sucess
Id (0x10000000) as ul = 0x10000000 - str_end - errno 0.
sucess
/* strtoul example */
#include <stdio.h> /* printf, NULL */
#include <stdlib.h> /* strtoul */
#include <errno.h>
signed int GetIdentifier(const char* idString)
{
char *str_end;
int id = -1;
errno = 0;
id = strtoul(idString, &str_end, 16);
if ( *str_end != '\0' || (errno == ERANGE))
{
printf("Error while converting Id (%s).\n", idString);
return -1;
}
// Return error if converted Id is more than 29-bit
if(id > 0x1FFFFFFF)
{
printf("Error: Id (%s) should fit on 29 bits (maximum value: 0x1FFFFFFF).\n", idString);
return -1;
}
printf("sucess\n");
return id;
}
int main ()
{
GetIdentifier("0x210000000");
GetIdentifier("0x10000000");
return 0;
}
值0x210000000
大于32位,并且在32位系统上,通常为32位long
,这意味着您无法使用strtoul
正确转换字符串。 您需要使用strtoull
并使用unsigned long long
,这保证至少为64位。
当然,C99中引入了long long
和strtoull
,因此您可能需要添加-std=c99
(或使用更高的标准,如C11)以使其正确构建。
看起来,问题在于您假设long
始终为 32位,而实际上将其定义为至少 32位。 有关标准整数类型的最小位大小,请参见此参考 。
在某些平台和编译器上, long
可以大于32位。 在64位硬件上的Linux是典型的此类平台,其中long
较大,即64位,当然足够容纳0x210000000
,这导致strtoul
没有给出错误。
您的代码在假设成功调用不会更改errno
的值时也不正确。 根据Linux errno
手册页 :
<errno.h>
头文件定义了整数变量errno
,该整数变量由系统调用和某些库函数在发生错误的情况下设置,以指示出问题所在。 它的值仅在指示错误的调用返回值时才有意义(即,大多数系统调用为-1;大多数库函数为-1或NULL); 成功的函数可以更改errno
。
(POSIX确实通过成功调用对errno
修改施加了更大的限制,但是Linux在很多情况下并不严格遵守POSIX,毕竟G NU的N ot U nix ...)
strtoul()
函数返回转换结果,或者,如果有前导减号,则返回转换结果的取反,表示为无符号值,除非原始(取整)值溢出。 在后一种情况下,strtoul()返回ULONG_MAX
并将errno设置为ERANGE
。 对于strtoull()来说也是如此(用ULLONG_MAX
代替ULONG_MAX
)。
除非strtoul
返回ULONG_MAX
,否则调用strtoul
后的errno
值是不确定的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.