Question
How can a numeric string be converted to an int in C? For example:
char s[] = "45";
int num = atoi(s);
Is atoi() the best approach, or is there a safer alternative that can detect invalid input and conversion errors?
Short Answer
You will learn how C converts text such as "45" into integer values, why atoi() is often insufficient, and how to use strtol() to validate input and detect errors such as invalid characters and values outside the int range.
Concept
C stores text in character arrays or string literals, while an int stores a binary numeric value. Converting a string to an integer means reading digit characters such as '4' and '5' and producing the numeric value 45.
atoi() can perform a basic conversion:
int value = atoi("45");
However, atoi() cannot reliably report failure. Both an invalid string and the valid string "0" may result in 0:
atoi("0"); // 0
atoi("hello"); // also 0
For input that may be invalid—such as user input, file contents, command-line arguments, or API data—use strtol() instead. It provides:
- An end pointer that shows where conversion stopped.
errnoreporting for values outside the range oflong.- A selectable numeric base, such as decimal (
10) or hexadecimal (16).
Because strtol() returns a long, check that the final result also fits in an int before casting it.
Mental Model
Think of conversion as a cashier reading a price label.
atoi()gives the cashier a label and asks for a number back. If the label is unreadable, the cashier still says0, without explaining why.strtol()gives the cashier a label and a checklist. The cashier can tell you where reading stopped, whether there were unexpected characters, and whether the number was too large.
When input comes from outside your program, you usually need the checklist.
Syntax and Examples
atoi() is simple but does not provide useful error detection:
#include <stdlib.h>
int number = atoi("45");
For validated conversion, use strtol():
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
int main(void) {
const char *text = "45";
char *end;
long value;
errno = 0;
value = strtol(text, &end, 10);
if (end == text) {
printf("No number was found.\n");
} else if (*end != '\0') {
printf("Unexpected characters after the number: %s\n", end);
} else if (errno == ERANGE || value < INT_MIN || value > INT_MAX) {
printf("Number is outside the int range.\n");
} else {
int number = (int)value;
printf("Converted value: %d\n", number);
}
return 0;
}
How the important arguments work:
long strtol(const char *text, char **end, int base);
text: the string to parse.end: receives the location where parsing stopped.base:10for decimal,16for hexadecimal, or0to infer a prefix such as0x.
For the string "45", strtol(text, &end, 10) returns 45 and makes end point to the terminating \0 character.
Step by Step Execution
Consider this program fragment:
const char *text = "123abc";
char *end;
long value = strtol(text, &end, 10);
Step by step:
strtol()starts at the first character,'1'.- It reads
'1','2', and'3'because they are valid base-10 digits. - It stops at
'a', because'a'is not a decimal digit. valuebecomes123.endpoints to the substring"abc".- Since
*end != '\0', the entire input was not a valid integer.
A complete check looks like this:
if (end == text) {
/* No digits were converted. */
} else if (*end != '\0') {
/* Some characters were left over. */
} else {
/* The complete string was converted. */
}
For "abc", end == text because conversion could not consume even one digit. For "123", *end == '\0' because conversion reached the end of the string.
Real World Use Cases
String-to-integer conversion is needed whenever a number arrives as text:
- Command-line arguments: Convert
./server 8080into an integer port number. - Configuration files: Read
max_connections=100and validate the value. - User input: Convert a menu selection or quantity entered in a terminal program.
- CSV and log files: Parse fields such as IDs, counts, years, and timestamps.
- Network protocols: Convert textual headers or message fields into numeric values.
In all of these cases, invalid input is possible. strtol() lets the program reject malformed values rather than silently using an unintended value.
Real Codebase Usage
In production C code, conversion is commonly placed in a small reusable helper function. This prevents repeated, incomplete checks throughout the program.
A typical pattern is:
- Parse into a wider type with
strtol(). - Check whether no digits were consumed.
- Check whether unparsed characters remain.
- Check
errnofor overflow or underflow. - Check the target type's range before casting.
- Return a success indicator instead of guessing a default value.
For example:
#include <errno.h>
#include <limits.h>
#include <stdbool.h>
#include <stdlib.h>
bool parse_int(const char *text, int *result) {
char *end;
long value;
if (text == NULL || result == NULL) {
return false;
}
errno = 0;
value = strtol(text, &end, 10);
if (end == text || *end != '\0') {
return false;
}
if (errno == ERANGE || value < INT_MIN || value > INT_MAX) {
return false;
}
*result = (int)value;
return true;
}
This is an example of a guard clause: invalid cases return early, so the successful path remains short and clear.
Common Mistakes
Using atoi() when validation matters
int value = atoi("invalid");
This produces 0, but 0 might also be a valid input. Use strtol() if the source is not guaranteed to be valid.
Ignoring trailing characters
char *end;
long value = strtol("42cats", &end, 10);
/* value is 42, but the whole string was not a valid integer. */
Check *end == '\0' when the input must contain only an integer.
Forgetting to check whether any digits were read
char *end;
long value = strtol("cats42", &end, 10);
Here, value is 0, but no number was parsed. Detect this with end == text.
Casting before checking the range
long value = strtol(text, &end, 10);
int number = (int)value; /* Potentially unsafe */
If value cannot fit in an int, the conversion may produce an incorrect result. Check against INT_MIN and INT_MAX first.
Not resetting errno
long value = strtol(text, &end, 10);
if (errno == ERANGE) {
/* May be reacting to an earlier library call. */
}
Set errno = 0 immediately before calling strtol() when you plan to inspect it.
Using the wrong base
strtol("101", &end, 2); /* 5, not 101 */
Use base 10 for ordinary decimal input. Use base 0 only when accepting C-style prefixes such as 0xFF.
Comparisons
| Function | Result type | Can detect invalid input? | Can detect range errors? | Best use |
|---|---|---|---|---|
atoi() | int | No | No reliable mechanism | Trusted, simple legacy code only |
strtol() | long | Yes, with end | Yes, with errno | Most integer parsing in C |
strtoll() | long long | Yes | Yes | Larger signed integer values |
strtoul() | unsigned long | Yes | Yes | Non-negative unsigned values |
sscanf() | depends on format | Partly | Less direct | Parsing multiple formatted fields |
strtol() is generally preferred over atoi() because it makes validation explicit. sscanf() can parse integers too, but strtol() is often easier to validate precisely when parsing a single number.
Whitespace is accepted at the beginning by both atoi() and strtol():
strtol(" -45", &end, 10); /* Parses -45 */
If leading whitespace should be rejected, check the first character yourself before conversion.
Cheat Sheet
#include <errno.h>
#include <limits.h>
#include <stdlib.h>
char *end;
long value;
errno = 0;
value = strtol(text, &end, 10);
if (end == text) {
/* No digits found. */
} else if (*end != '\0') {
/* Extra characters remain. */
} else if (errno == ERANGE) {
/* Value is outside long range. */
} else if (value < INT_MIN || value > INT_MAX) {
/* Value does not fit in int. */
} else {
int number = (int)value;
}
- Use
atoi()only when failure does not matter or input is fully trusted. - Use
strtol(text, &end, 10)for decimal integers. end == text: no number was converted.*end != '\0': trailing invalid characters exist.- Set
errno = 0before callingstrtol(). - Check
errno == ERANGEfor a value outsidelongrange. - Check
INT_MINandINT_MAXbefore convertinglongtoint.
FAQ
Is atoi() safe in C?
atoi() is memory-safe when passed a valid null-terminated string, but it is not suitable for validated input because it cannot report conversion errors or overflow reliably.
What should I use instead of atoi() in C?
Use strtol() for signed integer input. It can show whether conversion failed, whether extra characters remain, and whether the value was out of range.
Why does strtol() return long instead of int?
Returning long gives the function a wider result type on many platforms and provides a standard parsing interface. If you need an int, verify the result fits before casting.
How do I convert a string to an integer with no extra characters?
Call strtol(), then require both end != text and *end == '\0'.
Does strtol() accept negative numbers?
Yes. For example, strtol("-45", &end, 10) returns -45 when it is within range.
Does strtol() accept whitespace?
Yes. It skips leading whitespace. It does not automatically allow arbitrary trailing text; the end pointer lets you detect it.
How do I parse hexadecimal input in C?
Use base 16 for hexadecimal digits, or base 0 to accept prefixes such as 0xFF:
long value = strtol("0xFF", &end, 0);
Can I use strtol() for unsigned integers?
For values intended to be unsigned, use strtoul() and validate its result against the range of the target unsigned type.
Mini Project
Description
Create a small command-line-style parser for a TCP port number. A port is received as text, but the program must accept only complete decimal integers in the valid port range. This demonstrates parsing, validation, range checks, and clear error messages.
Goal
Write a function that converts a port string into an int only when it contains a valid decimal port number from 1 through 65535.
Requirements
Requirement 1
Keep learning
Related questions
2D Array Loop Order and Cache Performance in C
Learn why swapping nested loops changes 2D array performance in C, using row-major memory layout, cache locality, and practical benchmarks.
Array-to-Pointer Conversion in C and C++ Explained
Learn what array-to-pointer conversion means in C and C++, how array decay works, and how it differs from a pointer to an array.
Building More Fault-Tolerant Embedded C++ Applications for Radiation-Prone ARM Systems
Learn practical C++ and compile-time techniques to reduce soft-error damage in embedded ARM systems exposed to radiation.