C++ 为什么strtoul没有';你不能按预期工作吗?
嗨,我写了一个小测试程序来检查我写的将字符串(一个十六进制数)转换成无符号整数的函数是如何实现的,我发现根据我使用的编译器或系统,代码的行为会有所不同 我编译了下面的代码:C++ 为什么strtoul没有';你不能按预期工作吗?,c++,c,gcc,C++,C,Gcc,嗨,我写了一个小测试程序来检查我写的将字符串(一个十六进制数)转换成无符号整数的函数是如何实现的,我发现根据我使用的编译器或系统,代码的行为会有所不同 我编译了下面的代码: (1) ideone C++4.3.2 (2) centos6上的g++4.4.7(64位) (3) ubuntu12上的g++4.6.3(64位) (4) g++4.9.3在cygwin(32位)环境中 正如预期的那样(1)和(4)返回,并且它是正确的结果,因为第一个值“0x210000000”对于32位值来说太大了 Er
(1) ideone C++4.3.2
(2) centos6上的g++4.4.7(64位)
(3) ubuntu12上的g++4.6.3(64位)
(4) g++4.9.3在cygwin(32位)环境中 正如预期的那样(1)和(4)返回,并且它是正确的结果,因为第一个值“0x210000000”对于32位值来说太大了
Error while converting Id (0x210000000).
success
但是(2)和(3)返回
所以问题是为什么在不同的平台上使用不同的编译器构建相同的简单C代码会返回相同的结果。。。以及为什么'strtoul('0x210000000',…')没有将'errno'设置为'ERANGE',以表示位33到37超出范围
平台上的更多跟踪(3)给出:
Id(0x210000000)为ul=0x10000000-str_end-errno 0。
成功
Id(0x10000000)为ul=0x10000000-str_end-errno 0。
成功
/*典型的例子*/
#include/*printf,空*/
#包括/*strtoul*/
#包括
签名的int GetIdentifier(常量字符*idString)
{
字符*stru_end;
int id=-1;
errno=0;
id=strtoul(idString和stru_end,16);
if(*str_end!='\0'| |(errno==ERANGE))
{
printf(“转换Id(%s)时出错。\n”,idString);
返回-1;
}
//如果转换的Id大于29位,则返回错误
如果(id>0x1FFFFFF)
{
printf(“错误:Id(%s)应适合29位(最大值:0x1FFFFFF)。\n”,idString);
返回-1;
}
printf(“成功”\n);
返回id;
}
int main()
{
获取标识符(“0x210000000”);
获取标识符(“0x10000000”);
返回0;
}
值0x210000000
大于32位,在32位系统上long
通常为32位,这意味着您无法使用strtoul
正确转换字符串。您需要使用strtoull
和unsigned long-long
,保证至少为64位
当然,long-long
是在C99中引入的,因此您可能需要添加例如-std=C99
(或使用更高的标准,如C11)来正确构建它
问题似乎在于,您假设
long
始终是32位,而实际上它被定义为至少32位。有关标准整数类型的最小位大小,请参见
在某些平台和编译器上,
long
可以大于32位。64位硬件上的Linux是一个典型的平台,其中long
更大,即64位,这当然足够适合0x210000000
,这导致strtoul
没有给出错误。您的代码假设成功调用不会更改errno
的值也是不正确的。根据:
头文件定义整数变量errno
,
它由事件中的系统调用和一些库函数设置
指一个错误,以表明出了什么问题。它的价值是巨大的
只有调用的返回值指示错误(即,从
大多数系统调用;-1或大多数库函数为NULL)a
允许成功的函数更改errno
(POSIX确实通过成功调用对errno
修改施加了更大的限制,但Linux在许多情况下并不严格遵守POSIX,毕竟,GNU的NotUnix…)
:
strtoul()
函数返回转换的结果
或者,如果有一个前导减号,则为
表示为无符号值的转换,除非原始值
(非否定)值将溢出;在后一种情况下,strtoul()
返回ULONG\u MAX
并将errno设置为ERANGE
。一模一样
适用于stroull()(使用ULLONG\u MAX
而不是ULONG\u MAX
)
除非
strtoul
返回ULONG_MAX
,否则调用strtoul
后,errno
的值是不确定的。0x210000000=>超过4个八位字节,不是UL,但在任何一本优秀的初级C编程书中都很早就提到了不同的整数类型及其大小。通常,使用int
或任何其他有符号类型来存储十六进制数没有任何意义。你认为斯特图是做什么的?确保启用编译器警告。顺便说一句:如果(*str_end!='\0'||(errno==ERANGE))
无法检测到no转换,如id=strtoul(“,&str_end,16”)代码>建议如果(str_end==idString | |*str_end!='\0'| |(errno==ERANGE))
被否决,我提到预期结果是“0x210000000”的错误。我不是初学者,如果我想得到64位的值,我将使用'ull'函数。。。请重新阅读question@alexbuisson那我真的不明白你的问题。如果您期望to big值在32位系统上失败,那么您当然会期望它在64位系统上成功,对吗?对于案例2和案例3,您期望得到什么结果?我看不出除了在64位系统上不希望long
为64位之外,您可能会犯什么“错误”。与所有其他类型一样,long
类型可以有不同的大小,至少为32位,但可以更大,例如在使用GCC或Clang的64位系统上的Linux上,则long
为64位。为true,但添加检查“id!”=“ULONG_MAX”不会有帮助,因为在我的例子中,当“strtool”处理“0x210000000”时,在案例(2)和(3)中,我得到:“errno”等于0,“id”等于“0x10000000”,所以它不是“ULONG_MAX”@alexbuisson,errno
和strtool
手册页在您的安装上说了什么?OP对errno
的使用根据C117.5 3似乎是正确的
success
success
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;
}