4

I'm using atoi to convert a string integer value into integer. But first I wanted to test different cases of the function so I have used the following code

#include  <stdio.h>

int main(void)
{
    char *a ="01e";
    char *b = "0e1";
    char *c= "e01";
    int e=0,f=0,g=0;        
    e=atoi(a);
    f=atoi(b);
    g=atoi(c);
    printf("e= %d  f= %d  g=%d  ",e,f,g);
    return 0;
}

this code returns e= 1 f= 0 g=0 I don't get why it returns 1 for "01e"

Jean-François Fabre
  • 137,073
  • 23
  • 153
  • 219
MrsIl
  • 53
  • 1
  • 5
  • 1
    Don't use `atoi()`, it has poor error handling as per a bug in the C standard. Use `strtol` instead. – Lundin Sep 07 '17 at 07:52
  • 2
    Perhaps `a` simply considers `01e-0` which is a valid `1`? You would have to get to the bottom of the man page (and perhaps take a look at the source to get the ultimate answer). Save yourself the headache, use `strtol`. (the others are both valid zeros anyway, `0eWhatever` -- zero doesn't care how many decimal points it has *zero* = *zero*. `e01` (equivalent to `0e01`, again *zero* = *zero*. – David C. Rankin Sep 07 '17 at 08:14
  • 4
    What value did _you_ expect for `"01e"`? – Jabberwocky Sep 07 '17 at 08:41
  • @MichaelWalz I was expecting `0` because `a` was containing a letter, not only integer values. – MrsIl Sep 11 '17 at 07:20

2 Answers2

14

that's because atoi is an unsafe and obsolete function to parse integers.

  • It parses & stops when a non-digit is encountered, even if the text is globally not a number.
  • If the first encountered char is not a space or a digit (or a plus/minus sign), it just returns 0

Good luck figuring out if user input is valid with those (at least scanf-type functions are able to return 0 or 1 whether the string cannot be parsed at all as an integer, even if they have the same behaviour with strings starting with integers) ...

It's safer to use functions such as strtol which checks that the whole string is a number, and are even able to tell you from which character it is invalid when parsing with the proper options set.

Example of usage:

  const char *string_as_number = "01e";
  char *temp;
  long value = strtol(string_as_number,&temp,10); // using base 10
  if (temp != string_as_number && *temp == '\0')
  {
     // okay, string is not empty (or not only spaces) & properly parsed till the end as an integer number: we can trust "value"
  }
  else
  {
     printf("Cannot parse string: junk chars found at %s\n",temp);
  }
Jean-François Fabre
  • 137,073
  • 23
  • 153
  • 219
  • @Mrsil: Read the `atoi()` documentation here: http://en.cppreference.com/w/cpp/string/byte/atoi, there is no error checking in `atoi()`. Further on [this thread on SO at the bottom](https://stackoverflow.com/a/44563303/8051589) I compare the different outputs of `atoi()` and [`strtol()`](http://en.cppreference.com/w/cpp/string/byte/strtol) with a little example on ideone.com. – Andre Kampling Sep 07 '17 at 07:47
  • 2
    It works as documented: no error checking. I wouldn't call that "unsafe", there are usecases where `atoi()` is all you need. –  Sep 07 '17 at 07:51
  • @FelixPalmen Beginners should certainly not use it, as they won't be able to tell when it is safe to use. Also, better safe than sorry is usually the way to go for professionals as well - atoi() is banned by many coding standards such as MISRA-C and [CERT C](https://www.securecoding.cert.org/confluence/display/c/ERR34-C.+Detect+errors+when+converting+a+string+to+a+number). – Lundin Sep 07 '17 at 07:55
  • @Lundin and that's why I don't like many coding standards (no matter what language btw). *Banning* something should only be done when there's *no sensible use ever*. Something like `atoi()` might appear in an "*avoid*" rule, meaning don't use it unless you can show how it's the best option for your specific case. –  Sep 07 '17 at 07:59
  • 2
    @FelixPalmen `atoi` is okay for quick & dirty progs because the interface is easy: string in, number out, but not for "serious" programs. a bit like `gets`. problem is: hacks often become real commercial software without any modification :) – Jean-François Fabre Sep 07 '17 at 08:01
  • @FelixPalmen That's not how you program rugged software though. You might think that you've found a case where `atoi()` is perfectly safe. You use it to get a _tiny bit_ of extra performance over `strtol` (likely "premature optimization"). 3 years later someone comes in to maintain your code and passes a badly formatted string to the function. Boom, undefined behavior. – Lundin Sep 07 '17 at 08:03
  • 2
    @Jean-FrançoisFabre no, `gets()` is one of the few examples for an unconditional **ban** as using it is **always** a bug. `atoi()` just isn't suited for e.g. user input, but perfectly fine when you already know what can be in your string. –  Sep 07 '17 at 08:03
  • yes, it was an extreme example. `atoi` is unsafe because it doesn't detect errors, but `gets` can lead to buffer overflow attacks. – Jean-François Fabre Sep 07 '17 at 08:09
  • If `string_as_number[0] = 0`, this code does not catch that as an error. Suggest `if (temp != string_as_number && *temp == '\0')` – chux - Reinstate Monica Sep 07 '17 at 14:29
  • also: I must have read this technique in another SO post, they have forgotten the "empty" case as well... – Jean-François Fabre Sep 07 '17 at 14:35
  • @chux: what if user enters only spaces? that wouldn't be valid, but `*temp` would still hold `\0` right? (thanks for edit) – Jean-François Fabre Sep 07 '17 at 14:39
  • If there is no conversion, `temp == string_as_number` so only white-spaces results in no conversion. – chux - Reinstate Monica Sep 07 '17 at 14:40
  • 1
    For my code I do something like `if (temp == string_as_number) fail(); while (isspace((unsigned char) *temp)) temp++; if (*temp) fail();` to allow trailing ws as in `"123\n"`. – chux - Reinstate Monica Sep 07 '17 at 14:43
  • yeah, else `123\n` is properly parsed but the test fails as `*temp` isn't 0. I could edit but I don't want to overcomplexify my answer. – Jean-François Fabre Sep 07 '17 at 14:45
1

You are missing an opportunity: Write your own atoi. Call it Input2Integer or something other than atoi.

int Input2Integer( Str )

Note, you have a pointer to a string and you will need to establish when to start, how to calculate the result and when to end.

First: Set return value to zero.

Second: Loop over string while it is not null '\0'.

Third: return when the input character is not a valid digit.

Fourth: modify the return value based on the valid input character.

Then come back and explain why atoi works the way it does. You will learn. We will smile.

dcy665
  • 35
  • 4
  • not a valid digit, a space or a +/- sign. Not that easy. Also handle overflow by checking that you can convert the number to fit in the integer type. – Jean-François Fabre Sep 07 '17 at 14:37
  • Excuse me, did I say it was easy? No. I did not. I said it was a good opportunity to write their own version and figure out what is happening. atoi is opensource so they easily just 'read the source, Luke' if it comes to that. Also, atoi doesn't care about not a valid digit, it just returns. Nothing wrong with that. They can also decide to support +- as they see fit. What about learning don't you like? – dcy665 Sep 08 '17 at 01:01
  • Who said I don't like your answer? I was just commenting on "digits". – Jean-François Fabre Sep 08 '17 at 05:17