58

http://www.cplusplus.com/reference/clibrary/cstdlib/atoi/

Return Value

On success, the function returns the converted integral number as an int value. If no valid conversion could be performed, a zero value is returned. If the correct value is out of the range of representable values, INT_MAX or INT_MIN is returned.

So how I differ between atoi("poop") and atoi("0") and atoi("0000000")

Yes I can loop and check for all zeroes in case I get 0 result, but isn't there a better way?

Notice: I use ANSI C89

kame
  • 20,848
  • 33
  • 104
  • 159
Nahum
  • 6,959
  • 12
  • 48
  • 69
  • 9
    [cplusplus.com is wrong](http://jcatki.no-ip.org/fncpp/cplusplus.com) about `atoi`, it does not detect errors at all. Don't trust that site. – Fred Foo Mar 05 '13 at 16:48
  • The C89, C99 and C11 standards say nothing about the value returned by `atoi()` when the correct value is out of range. The C++11 standard says even less (it lists `atoi` in two tables, and that's all!). The statements from http://cplusplus.com/ are essentially wishful thinking and/or common implementations — not guaranteed by any standard. – Jonathan Leffler Aug 31 '13 at 04:37
  • TL;DR: Use http://en.cppreference.com/w/cpp/string/byte/atoi instead. – Kuba hasn't forgotten Monica Sep 03 '15 at 16:17

3 Answers3

52

That's one of the reasons atoi is sometimes considered unsafe. Use strtol / strtoul instead. And if you have it use strtonum.

The function atoi is more dangerous than you might think. The POSIX standard says:

If the value cannot be represented, the behavior is undefined.

The C99 standard says this also:

7.20.1

The functions atof, atoi, atol, and atoll need not affect the value of the integer expression errno on an error. If the value of the result cannot be represented, the behavior is undefined.

Community
  • 1
  • 1
cnicutar
  • 178,505
  • 25
  • 365
  • 392
20

As described by @cnicutar and @ouah, atoi can't distinguish a valid 0 from an invalid string making the strtol family better options.

But Why? and How? First understand that both atoi and strtol only convert the initial set of numbers in a string to numeric values. Any trailing non-numeric characters are simply ignored. strtol can be used to check for invalid strings because in addition to a numeric value, it also returns a pointer to the end of the numeric portion of the string. Thus if this end pointer still refers to the start of the original string, you can tell that there was an error and no characters from the string were converted.

There are a few of other subtleties, as seen in the code example:

long lnum;
int num;
char *end;

errno = 0;

lnum = strtol(in_str, &end, 10);        //10 specifies base-10
if (end == in_str)     //if no characters were converted these pointers are equal
    fprintf(stderr, "ERROR: can't convert string to number\n");

//If sizeof(int) == sizeof(long), we have to explicitly check for overflows
if ((lnum == LONG_MAX || lnum == LONG_MIN) && errno == ERANGE)  
    fprintf(stderr, "ERROR: number out of range for LONG\n");

//Because strtol produces a long, check for overflow
if ( (lnum > INT_MAX) || (lnum < INT_MIN) )
    fprintf(stderr, "ERROR: number out of range for INT\n");

//Finally convert the result to a plain int (if that's what you want)
num = (int) lnum;

Note: If you are sure the input string will be within the valid int range, you can eliminate lnum and simply cast strtol's return directly: num = (int) strtolen(in_str, &end, 10);

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Bryan P
  • 5,900
  • 5
  • 34
  • 49
  • 5
    There's a lot that's good there, but...if `sizeof(int) == sizeof(long)`, you haven't detected overflow. You should set `errno = 0;` before calling `strtol()`, and then you need to test for `((lnum == LONG_MAX || lnum == LONG_MIN) && errno == ERANGE)` to spot overflow. Although it does the job, `strtol()` is actually very hard to use correctly — even harder than you demonstrate. – Jonathan Leffler Aug 31 '13 at 04:29
  • 1
    @JonathanLeffler Great point. I have updated my answer accordingly, Thanks. – Bryan P Aug 31 '13 at 04:40
  • Concerning `if ( (lnum > INT_MAX) || (lnum < INT_MIN) )`, for symmetry with `long` overflow, that `if()` block should also `errno = ERANGE;` and set the `lnum` to `INT_MAX` or `INT_MIN`. – chux - Reinstate Monica Aug 10 '17 at 21:55
7

You cannot.

atoi cannot detect errors. If the result cannot be represented, atoi invokes undefined behavior. Use strtol instead of atoi.

Secure CERT coding advises to use strtol instead of atoi, read:

INT06-C. Use strtol() or a related function to convert a string token to an integer

Mat
  • 202,337
  • 40
  • 393
  • 406
ouah
  • 142,963
  • 15
  • 272
  • 331