简体   繁体   English

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

[英]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 longstrtoull ,因此您可能需要添加-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 variable errno , 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() returns ULONG_MAX and sets errno to ERANGE . 在后一种情况下,strtoul()返回ULONG_MAX并将errno设置为ERANGE Precisely the same holds for strtoull() (with ULLONG_MAX instead of ULONG_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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM