[英]Why strtoul doesn't work as expected?
Hi I wrote a small test program to check how the function I wrote to convert string (an hexadecimal number) into a unsigned integer and I found that the code behave differently depending on the compiler or system I use. 嗨,我编写了一个小型测试程序,以检查编写的函数如何将字符串(十六进制数)转换为无符号整数,并且我发现代码的行为取决于我使用的编译器或系统。
I compiled the code below on: 我在下面编译了以下代码:
(1) ideone C++4.3.2 https://ideone.com/LlcNWw (1)ideone C ++ 4.3.2 https://ideone.com/LlcNWw
(2) g++ 4.4.7 on a centos6 (64bits) (2)在centos6(64位)上的g ++ 4.4.7
(3) g++ 4.6.3 on an ubuntu12 (64bits) (3)ubuntu12上的g ++ 4.6.3(64位)
(4) g++ 4.9.3 in a cygwin (32bits) environment (4)在Cygwin(32位)环境中的g ++ 4.9.3
As expected (1) and (4) return AND IT'S exactly the correct result as the 1st value '0x210000000' is to big for a 32bit value.... 如预期的那样(1)和(4)返回AND它是正确的结果,因为第一个值'0x210000000'对于32位值来说很大。
Error while converting Id (0x210000000).
success
but (2) and (3) return 但是(2)和(3)返回
success
success
SO THE QUESTION is why the same simple C code build on different platform with different compiler return the same result... and Why 'strtoul("0x210000000", ....)' doesn't set 'errno' to 'ERANGE' to said that the bit 33 to 37 are out of range. 所以问题是为什么在不同平台上使用不同的编译器来构建相同的简单C代码会返回相同的结果...以及为什么'strtoul(“ 0x210000000”,....)'不会将'errno'设置为'ERANGE'表示位33到37超出范围。
more trace on a the platform (3) give: 在平台上的更多跟踪信息(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;
}
The value 0x210000000
is larger than 32 bits, and on 32 bit systems long
is usually 32 bits which means you can't use strtoul
to convert the string correctly. 值
0x210000000
大于32位,并且在32位系统上,通常为32位long
,这意味着您无法使用strtoul
正确转换字符串。 You need to use strtoull
and use unsigned long long
which is guaranteed to be at least 64 bits. 您需要使用
strtoull
并使用unsigned long long
,这保证至少为64位。
Of course, long long
and strtoull
was introduced in C99, so you might need to add eg -std=c99
(or use a later standard like C11) to have it build correctly. 当然,C99中引入了
long long
和strtoull
,因此您可能需要添加-std=c99
(或使用更高的标准,如C11)以使其正确构建。
The problem, it seems, is that you assume that long
is always 32 bits, when in fact it's defined to be at least 32 bits. 看起来,问题在于您假设
long
始终为 32位,而实际上将其定义为至少 32位。 See eg this reference for the minimum bit-size of the standard integer types. 有关标准整数类型的最小位大小,请参见此参考 。
On some platforms and compilers, long
can be bigger than 32 bits. 在某些平台和编译器上,
long
可以大于32位。 Linux on 64-bit hardware is a typical such platform where long
is bigger, namely 64 bits, which is of course well enough to fit 0x210000000
, which leads to strtoul
not giving an error. 在64位硬件上的Linux是典型的此类平台,其中
long
较大,即64位,当然足够容纳0x210000000
,这导致strtoul
没有给出错误。
Your code is also incorrect in assuming a successful call will not change the value of errno
. 您的代码在假设成功调用不会更改
errno
的值时也不正确。 Per the Linux errno
man page : 根据Linux
errno
手册页 :
The
<errno.h>
header file defines the integer variableerrno
, which is set by system calls and some library functions in the event of an error to indicate what went wrong.<errno.h>
头文件定义了整数变量errno
,该整数变量由系统调用和某些库函数在发生错误的情况下设置,以指示出问题所在。 Its value is significant only the return value of the call indicated an error (ie, -1 from most system calls; -1 or NULL from most library functions);它的值仅在指示错误的调用返回值时才有意义(即,大多数系统调用为-1;大多数库函数为-1或NULL); a function that succeeds is allowed to change
errno
.成功的函数可以更改
errno
。
(POSIX does place greater restrictions on errno
modification by successful calls, but Linux doesn't strictly adhere to POSIX in many cases, and after all, G NU's N ot U nix...) (POSIX确实通过成功调用对
errno
修改施加了更大的限制,但是Linux在很多情况下并不严格遵守POSIX,毕竟G NU的N ot U nix ...)
The strtoul
man page states : strtoul
手册页指出 :
The
strtoul()
function returns either the result of the conversion or, if there was a leading minus sign, the negation of the result of the conversion represented as an unsigned value, unless the original (nonnegated) value would overflow;strtoul()
函数返回转换结果,或者,如果有前导减号,则返回转换结果的取反,表示为无符号值,除非原始(取整)值溢出。 in the latter case, strtoul() returnsULONG_MAX
and sets errno toERANGE
.在后一种情况下,strtoul()返回
ULONG_MAX
并将errno设置为ERANGE
。 Precisely the same holds for strtoull() (withULLONG_MAX
instead ofULONG_MAX
).对于strtoull()来说也是如此(用
ULLONG_MAX
代替ULONG_MAX
)。
Unless strtoul
returned ULONG_MAX
, the value of errno
after a call to strtoul
is indeterminate. 除非
strtoul
返回ULONG_MAX
,否则调用strtoul
后的errno
值是不确定的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.