The behaviour of atoi("X") is undefined.
[...]
I'm not quite sure about that. Here's what the C99 standard says:
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.
...
The atoi, atol, and atoll functions convert the initial portion of
the string pointed to by nptr to int, long int, and long long int
representation, respectively. Except for the behavior on error,
they are equivalent to
atoi: (int)strtol(nptr, (char **)NULL, 10)
Certainly the behavior of atoi("99999999999999999999") is undefined
(assuming 99999999999999999999 exceeds INT_MAX). But
strtol("X", NULL, 10) returns 0 and sets errno to ERANGE.
One reasonable interpretation of the standard's wording is that the
behavior of atoi() on error (either an out-of-range argument or a
conversion failure) is undefined. Another reasonable interpretation,
I think, is that atoi("X") returns 0 and doesn't necessarily set
errno.