为什么strtoul不能按预期工作?

时间:2016-03-02 10:08:50

标签: c++ c gcc

您好我写了一个小测试程序来检查我编写的函数如何将字符串(十六进制数)转换为无符号整数,我发现代码的行为有所不同,具体取决于我使用的编译器或系统。

我编写了以下代码:
(1)ideone C ++ 4.3.2 https://ideone.com/LlcNWw
(2)g ++ 4.4.7在centos6(64bits)上 (3)ubuntu12(64位)上的g ++ 4.6.3
(4)在cygwin(32位)环境中的g ++ 4.9.3

正如预期的那样(1)和(4)返回AND IT正是正确的结果,因为第一个值'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;
}

2 个答案:

答案 0 :(得分:8)

0x210000000大于32位,而32位系统long通常为32位,这意味着您无法使用strtoul正确转换字符串。您需要使用strtoull并使用保证至少为64位的unsigned long long

当然,C99中引入了long longstrtoull,因此您可能需要添加例如-std=c99(或使用类似C11的后续标准)使其正确构建。

问题似乎是你假设long 总是 32位,实际上它被定义为至少 32位。参见例如this reference表示标准整数类型的最小位大小。

在某些平台和编译器上,long可能大于32位。 64位硬件上的Linux是一个典型的平台,其中long更大,即64位,这当然足以适合0x210000000,这导致strtoul没有给出错误

答案 1 :(得分:1)

假设成功通话不会改变errno的值,您的代码也是错误的。根据{{​​3}}:

  

<errno.h>头文件定义整数变量errno,   这是由系统调用和事件中的一些库函数设置的   一个错误,表明出了什么问题。它的价值很重要   只有呼叫的返回值表示一个错误(即-1来自   大多数系统调用;大多数库函数的-1或NULL);的一   成功的功能可以更改errno

(POSIX确实通过成功调用对errno修改施加了更大的限制,但Linux在很多情况下并没有严格遵守POSIX,毕竟 G NU的 N ot U nix ...)

Linux errno man page

  

strtoul()函数返回转换结果   或者,如果有一个领先的减号,则否定结果   转换表示为无符号值,除非原始值   (非整数)值会溢出;在后一种情况下,strtoul()   返回ULONG_MAX并将errno设置为ERANGE。完全一样   持有strtoull()(ULLONG_MAX代替ULONG_MAX)。

除非strtoul返回ULONG_MAX,否则调用errnostrtoul的值不确定。