3

I am trying to do an hexadecimal to integer conversion on a 32 bit machine. Here is the code I am testing with,

int main(int argc,char **argv)
{
    char *hexstring = "0xffff1234";
    long int n;

    fprintf(stdout, "Conversion results of string: %s\n", hexstring);
    n = strtol(hexstring, (char**)0, 0); /* same as base = 16 */
    fprintf(stdout, "strtol = %ld\n", n);
    n = sscanf(hexstring, "%x", &n);
    fprintf(stdout, "sscanf = %ld\n", n);
    n = atol(hexstring);
    fprintf(stdout, "atol = %ld\n", n);
    fgetc(stdin);

    return 0;
  }

This is what I get:

 strtol = 2147483647 /* = 0x7fffffff -> overflow!! */
 sscanf = 1 /* nevermind */
 atol = 0   /* nevermind */

As you see, with strtol I get an overflow (I also checked with errno), although I would expect none to happen, since 0xffff1234 is a valid integer 32bit value. I would either expect 4294906420 or else -60876

What am I missing?

user1725145
  • 3,993
  • 2
  • 37
  • 58
jpmuc
  • 1,092
  • 14
  • 30
  • Are you sure that setting the base number to 0 is the as 16. I mean replace *strtol(hexstring, (char**)0, 0)* by *strtol(hexstring, (char**)0, **16**)* – A.G. Jul 24 '12 at 09:48
  • @A.G. setting the base to 0 means `strtol` tries to figure it out itself from the start of the input string, whether to use base 16, 10 or 8. – Daniel Fischer Jul 24 '12 at 10:14

2 Answers2

7

If you don't want the overflow effect, don't use the signed variant of the function. Use strtoul() instead.

With the following code:

#include <stdio.h>
int main(int argc,char **argv) {
    char *hexstring = "0xffff1234";
    long sn;
    unsigned long un;

    fprintf(stdout, "Conversion results of string: %s\n", hexstring);

    sn = strtoul(hexstring, (char**)0, 0);
    fprintf(stdout, "strtoul   signed = %ld\n", sn);

    un = strtoul(hexstring, (char**)0, 0);
    fprintf(stdout, "strtoul unsigned = %lu\n", un);

    return 0;
}

I get:

Conversion results of string: 0xffff1234
strtoul   signed = -60876
strtoul unsigned = 4294906420

You cannot control this when calling strtol() and its brethren since the behaviour is within those functions. The standard has this to say:

The strtol, strtoll, strtoul, and strtoull functions return the converted value, if any. If no conversion could be performed, zero is returned. If the correct value is outside the range of representable values, LONG_MIN, LONG_MAX, LLONG_MIN, LLONG_MAX, ULONG_MAX, or ULLONG_MAX is returned (according to the return type and sign of the value, if any), and the value of the macro ERANGE is stored in errno.

paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
  • That's the correct solution, alright. Meh, I had it right in front of me on the docs, but didn't see it :D +1 anyway. :) – ATaylor Jul 24 '12 at 09:51
  • @juampa The overflow happens inside the function, I believe. Why else would there be two functions? If you, however, output the content of 'n' as signed, you'll get -60876...meh, his edit beat me to it :D – ATaylor Jul 24 '12 at 09:52
3

The minimum range of long is -2147483647 to 2147483647. The range of long on your implementation is likely -2147483648 to 2147483647.

The definition of the strtol() says that if the converted value is outside the range of long, LONG_MIN or LONG_MAX is returned according to the sign of the converted value, and ERANGE is stored in errno. In this case, the converted value 0xffff1234 is outside the range - it's greater than LONG_MAX, so LONG_MAX is returned.

By the way, your call to sscanf() is overwriting the converted value in n with the return value of sscanf(), which is 1 indicating that there was 1 successful conversion.

caf
  • 233,326
  • 40
  • 323
  • 462