Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

atoi vs atol vs strtol vs strtoul vs sscanf

Tags:

c

I'm trying to figure out from a command line being parsed, which function would be best to convert either a decimal, hexadecimal, or octal number to an int the best — without knowing the input beforehand.

The goal then is to use a single function that recognizes the different types of inputs and assign that to its integer (int) value which can then be used so:

./a.out 23 0xC4 070

could print

23
196 /*hexadecimal*/
56  /*octal*/

The only issue that I can see is the parsing to find the difference between a decimal integer and an octal.

Side question, is this stable for converting the string to an integer for use?

like image 404
user3362954 Avatar asked Apr 04 '14 14:04

user3362954


2 Answers

which function would be best to convert either a decimal, hexadecimal, or octal number to an int the best (?)

To convert such text to int, recommend long strtol(const char *nptr, char **endptr, int base); with additional tests when converting to int, if needed.

Use 0 as the base to assess early characters in steering conversion as base 10, 16 or 8.
@Mike Holt

Convert text per:
Step 1: Optional whitespaces like `' '`, tab, `'\n'`, ... .
Step 2: Optional sign: `'-'` or `'+'`.
Step 3:
  0x or 0X followed by hex digits--> hexadecimal  
  0 --> octal  
  else --> decimal  

Sample code

#include <errno.h>
#include <limits.h>
#include <stdlib.h>

int mystrtoi(const char *str) {
  char *endptr;
  errno = 0;
  //                                   v--- determine conversion base
  long long_var = strtol(str, &endptr, 0);
  //   out of range   , extra junk at end,  no conversion at all   
  if (errno == ERANGE || *endptr != '\0' || str == endptr) {
    Handle_Error();
  }

  // Needed when `int` and `long` have different ranges
  #if LONG_MIN < INT_MIN || LONG_MAX > INT_MAX
  if (long_var < INT_MIN || long_var > INT_MAX) {
    errno = ERANGE;
    Handle_Error();
  }
  #endif

  return (int) long_var;
}

atoi vs atol vs strtol vs strtoul vs sscanf ... to int

atoi()
Pro: Very simple.
Pro: Convert to an int.
Pro: In the C standard library.
Pro: Fast.
Con: On out of range errors, undefined behavior. @chqrlie
Con: Handle neither hexadecimal nor octal.

atol()
Pro: Simple.
Pro: In the C standard library.
Pro: Fast.
Con: Converts to an long, not int which may differ in size.
Con: On out of range errors, undefined behavior.
Con: Handle neither hexadecimal nor octal.

strtol()
Pro: Simple.
Pro: In the C standard library.
Pro: Good error handling.
Pro: Fast.
Pro: Can handle binary. (base 2 to base 36)
Con: Convert to an long, not int which may differ in size.

strtoul()
Pro: Simple.
Pro: In the C standard library.
Pro: Good error handling.
Pro: Fast.
Pro: Can handle binary.
---: Does not complain about negative numbers.
Con: Converts to an unsigned long, not int which may differ in size.

sscanf(..., "%i", ...)
Pro: In the C standard library.
Pro: Converts to int.
---: Middle-of-the-road complexity.
Con: Potentially slow.
Con: OK error handling (overflow is not defined).

All suffer/benefit from locale settings. §7.22.1.4 6 “In other than the "C" locale, additional locale-specific subject sequence forms may be accepted.”


Additional credits:
@Jonathan Leffler: errno test against ERANGE, atoi() decimal-only, discussion about errno multi-thread concern.
@Marian Speed issue.
@Kevin Library inclusiveness.


For converting short, signed char, etc., consider strto_subrange().

like image 116
chux - Reinstate Monica Avatar answered Sep 25 '22 13:09

chux - Reinstate Monica


It is only sensible to consider strtol() and strtoul() (or strtoll() or strtoull() from <stdlib.h>, or perhaps strtoimax() or strtoumax() from <inttypes.h>) if you care about error conditions. If you don't care about error conditions on overflow, any of them could be used. Neither atoi() nor atol() nor sscanf() gives you control if the values overflow. Additionally, neither atoi() nor atol() provides support for hex or octal inputs (so in fact you can't use those to meet your requirements).

Note that calling the strtoX() functions is not entirely trivial. You have to set errno to 0 before calling them, and pass a pointer to get the end location, and analyze carefully to know what happened. Remember, all possible return values from these functions are valid outputs, but some of them may also indicate invalid inputs — and errno and the end pointer help you distinguish between them all.

If you need to convert to int after reading the value using, say, strtoll(), you can check the range of the returned value (stored in a long long) against the range defined in <limits.h> for int: INT_MIN and INT_MAX.

For full details, see my answer at: Correct usage of strtol().

Note that none of these functions tells you which conversion was used. You'll need to analyze the string yourself. Quirky note: did you know that there is no decimal 0 in C source; when you write 0, you are writing an octal constant (because its first digit is a 0). There are no practical consequences to this piece of trivia.

like image 31
Jonathan Leffler Avatar answered Sep 24 '22 13:09

Jonathan Leffler