I am trying to figure out the function atoi()
from stdlib.h
. As per K&R, it looks like the following:
int atoi(char s[]) {
int n, i;
n = 0;
for (i = 0; s[i] >= '0' && s[i] <= '9'; ++i)
n = 10 * n + (s[i] - '0');
return n;
}
As I understand, the atoi()
function, from stdlib.h
, should get the string of any characters as an input and literally output only digits like the following:
Code 1:
#include <stdio.h>
#include <stdlib.h>
int main(void) {
printf("%i", atoi(" -123junk"));
printf("%i", atoi("0"));
printf("%i", atoi("junk")); // no conversion can be performed
printf("%i", atoi("2147483648")); // UB: out of range of int
}
Output:
-123
0
0
-2147483648
However, in my program, I am trying to provide the string as input and get the digits only as output:
Code 2:
#include <stdio.h>
#include <stdlib.h>
int main() {
int c, i;
char s[i];
for (i = 0; (c = getchar()) != '\n'; ++i)
s[i] = c;
s[i] = '\n';
s[++i] = '\0';
printf("%i", atoi(s));
}
When executing on machine:
pi@host:~/new$ cc new.c
pi@host:~/new$ a.out
21412421How it is
0
I am getting incorrect values as an output.
Questions:
1) As per Code 1, printf("%i", atoi(" -123junk"))
, it looks like the function atoi()
can receive string as an argument and return single integer number that represents concatenation of numeric values of the digits in the input string, isn't it?
2) What does the atoi()
, from stdlib.h
, return?
3) How to fix the function main()
from Code 2, in order to get characters from stdin
, write into the array, call the function atoi()
, provide array as an argument, receive, literally, digits in output?
4) As per K&R example of the function atoi()
, "the expression (s[i] - '0')
is the numeric value of the character stored in s[i]
", but, why do we need to add 10 * n
part, moreover, to assign n
to 0
before it, as n * 0 = 0
, hence, n * 10 = 0
, which means, that the n * 10
will always be zero in the assignment statement n = 10 * n + (s[i] - '0');
, hence, why do we need it?
5) If the atoi() is returning one integer, how can the result of printf("%i", atoi(" -123junk"));
return a string with numbers -123
? In other words, am I understand it correctly: The function atoi()
is called within function printf()
with the " -123junk"
as an argument. Function atoi()
returns integer, ONLY one integer, which is something like n = 10 * n + (s[i] - '0');
than, how it can be expanded in -123
??
Q1) your version of atoi()
is too simple, the standard version ignores leading whitespace characters and handles an optional sign before the number. atoi(" -123junk")
should evaluate to -123
.
Q2) atoi
is a standard function defined with the prototype int atoi(const char *s);
it returns an integer.
Q3) There are a few mistakes in Code 2
:
char
array s
as i
, which is uninitialized. you should instead define the array with a reasonably large value such as 64
,EOF
to stop the loop in case end of file is encountered without a newline.Here is a modified version:
#include <stdio.h>
#include <stdlib.h>
int main() {
char s[64];
size_t i;
int c;
for (i = 0; i < sizeof(s) - 1 && (c = getchar()) != EOF;) {
s[i++] = c;
if (c == '\n')
break;
}
s[i] = '\0';
printf("%i", atoi(s));
return 0;
}
Q4) the expression n = 10 * n + (s[i] - '0')
is evaluated for each new digit found in the string. It is indeed slightly inefficient to multiply the current value by 10 as long as no non-zero digit has been encountered, but writing the function this way is simple.
To avoid these useless multiplications, here is an alternative:
int atoi(const char *s) {
int n = 0;
size_t i = 0;
while (s[i] == '0')
i++;
if (s[i] >= '1' && s[i] <= '9') {
n = s[i++] - '0';
while (s[i] >= '0' && s[i] <= '9')
n = 10 * n + (s[i++] - '0');
}
return n;
}
But this function is more cumbersome and might actually be less efficient that the simple version. Try and benchmark both on your system.
For completeness, here is a full portable version using ctype.h>
that handles optional initial whitespace, and an optional sign. It also handles overflow with defined behavior, although the standard version of atoi()
is not required to do so.
#include <limits.h>
#include <stdio.h>
int atoi(const char *s) {
int n = 0, d;
/* skip optional initial white space */
while (isspace((unsigned char)*s))
s++;
if (*s == '-') {
/* convert negative number */
s++;
while (isdigit((unsigned char)*s)) {
d = (*s++ - '0');
/* check for potential arithmetic overflow */
if (n < INT_MIN / 10 || (n == INT_MIN / 10 && -d < INT_MIN % 10)) {
n = INT_MIN;
break;
}
n = n * 10 - d;
}
} else {
/* ignore optional positive sign */
if (*s == '+')
s++;
while (isdigit((unsigned char)*s)) {
d = (*s++ - '0');
/* check for potential arithmetic overflow */
if (n > INT_MAX / 10 || (n == INT_MAX / 10 && d > INT_MAX % 10)) {
n = INT_MAX;
break;
}
n = n * 10 + d;
}
}
return n;
}
int main(int argc, char *argv[]) {
int i, n;
for (i = 1; i < argc; i++) {
n = atoi(argv[i]);
printf("\"%s\" -> %d\n", argv[i], n);
}
return 0;
}