为什么 strtoul 没有按预期工作?
Why strtoul doesn't work as expected?
写了一个小型测试程序来检查我编写的函数如何将字符串(十六进制数)转换为无符号整数,我发现代码的行为因我使用的编译器或系统而异。
我编译了下面的代码:
(1) IDE C++4.3.2 https://ideone.com/LlcNWw
(2) G++ 4.4.7 在 CentOS6 (64 位)
(3) Ubuntu 上的 G++ 4.6.3 12(64 位)
(4) G++ 4.9.3 在 Cygwin(32 位)环境中
正如预期的那样 (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 != ' ' || (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("sucessn");
return id;
}
int main ()
{
GetIdentifier("0x210000000");
GetIdentifier("0x10000000");
return 0;
}
值0x210000000
大于 32 位,在 32 位系统上,long
通常为 32 位,这意味着您无法使用 strtoul
正确转换字符串。您需要使用strtoull
并使用保证至少为 64 位的unsigned long long
。
当然,long long
和strtoull
是在 C99 中引入的,因此您可能需要添加例如 -std=c99
(或使用C11等更高标准)才能正确构建它。
问题似乎在于,您假设long
始终是 32 位,而实际上它被定义为至少 32 位。例如,请参阅此参考,了解标准整数类型的最小位大小。
在某些平台和编译器上,long
可以大于 32 位。64位硬件上的Linux是一个典型的平台,其中long
更大,即64位,这当然足以适应0x210000000
,这导致strtoul
不会给出错误。
您的代码也不正确,假设成功的调用不会更改 errno
的值。 根据 Linux errno
手册页:
<errno.h>
头文件定义了整数变量errno
, 由事件中的系统调用和一些库函数设置 错误以指示出了什么问题。 它的价值是显着的 只有调用的返回值指示错误(即,-1 from 大多数系统调用;-1 或大多数库函数中的 NULL );一个 允许成功的函数更改errno
。
(POSIX 确实对成功调用的errno
修改施加了更大的限制,但 Linux 在很多情况下并没有严格遵守 POSIX,毕竟 GNU 的 Not Unix......
strtoul
手册页指出:
strtoul()
函数返回转换结果 或者,如果有前导减号,则否定 表示为无符号值的转换,除非原始值 (非否定)值将溢出;在后一种情况下,strtoul() 返回ULONG_MAX
并将 errno 设置为ERANGE
。 一模一样 对于 strtoull() 的保留(使用ULLONG_MAX
而不是ULONG_MAX
)。
除非strtoul
返回ULONG_MAX
,否则调用strtoul
后errno
的值是不确定的。
- QSqlquery prepare()和bindvalue()不工作
- 导入库可以跨dll版本工作吗
- 以螺旋方式打印矩阵的程序.(工作不好)
- 对象指针在c++中是如何工作的
- 为什么在Windows上的VS 2019和Clang 9中"size_t"在没有标题的情况下工作
- VSOMEIP-2个设备之间的通信(TCP/UDP)不工作
- 为字符串中每 N 个字符插入空格的函数没有按照我认为的方式工作?
- C++为线程工作动态地分割例程
- 为什么我的 std::ref 无法按预期工作?
- 布尔比较运算符是如何在C++中工作的
- SampleConsensusPrerejective(ext.RANSAC)是如何真正工作的
- 不确定要在我的main中放入什么才能使我的代码正常工作
- 为什么std::condition_variable notify_all的工作速度比notify_one快(对于随机请
- <<操作员在下面的行中工作
- 有人能解释一下为什么下界是这样工作的吗C++的
- ExtractIconEx:可以工作,但偶尔会崩溃
- C++中的memset函数工作不正常
- 当我在第一个循环中使用"auto"时,它工作正常,但是使用"int"它会给出错误,为什么?
- 链表c++插入,所有情况都已检查,但没有任何工作
- 为什么 strtoul 没有按预期工作?