alexbuisson
alexbuisson

Reputation: 8479

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
(2) g++ 4.4.7 on a centos6 (64bits)
(3) g++ 4.6.3 on an ubuntu12 (64bits)
(4) g++ 4.9.3 in a cygwin (32bits) environment

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....

Error while converting Id (0x210000000).
success

but (2) and (3) return

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.

more trace on a the platform (3) give:

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;
}

Upvotes: 0

Views: 2149

Answers (2)

Some programmer dude
Some programmer dude

Reputation: 409266

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. You need to use strtoull and use unsigned long long which is guaranteed to be at least 64 bits.

Of course, long long and strtoull was introduced in C99, so you might need to add e.g. -std=c99 (or use a later standard like C11) to have it build correctly.


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. See e.g. this reference for the minimum bit-size of the standard integer types.

On some platforms and compilers, long can be bigger than 32 bits. 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.

Upvotes: 8

Andrew Henle
Andrew Henle

Reputation: 1

Your code is also incorrect in assuming a successful call will not change the value of errno. Per the Linux errno man page:

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. Its value is significant only the return value of the call indicated an error (i.e., -1 from most system calls; -1 or NULL from most library functions); a function that succeeds is allowed to change 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, GNU's Not Unix...)

The strtoul man page states:

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; in the latter case, strtoul() returns ULONG_MAX and sets errno to ERANGE. Precisely the same holds for strtoull() (with ULLONG_MAX instead of ULONG_MAX).

Unless strtoul returned ULONG_MAX, the value of errno after a call to strtoul is indeterminate.

Upvotes: 1

Related Questions