Converting a string to a number in C is one of those tasks that looks simple until you hit the details: whitespace, signs, invalid characters, overflow, and whether the string contains an integer or a floating-point value. C gives you several ways to do it, and the right choice depends on how strict you want parsing to be and how much error handling you need.
The short version is this:
- Use
strtol,strtoul,strtod, or related functions when you want real validation. - Use
sscanfonly for small, controlled inputs where convenience matters more than precision. - Avoid
atoiand friends in new code because they do not report parsing errors cleanly. - If you need to understand exactly where conversion stopped, prefer the
endptrpattern.
The main conversion options
C does not have a single universal string_to_number() function. Instead, the standard library gives you families of functions for different numeric types. The most useful ones live in <stdlib.h>.
| Input type | Recommended function | Notes |
|---|---|---|
| Signed integer | strtol | Best default for validated integer parsing |
| Unsigned integer | strtoul | Use for non-negative values only |
| Long long integer | strtoll | Use when values may exceed long |
| Unsigned long long integer | strtoull | For large unsigned ranges |
| Floating-point | strtod | Best general-purpose float parser |
| Float/long double | strtof, strtold | More specific floating-point variants |
If you only remember one idea, remember this: parse with a function that lets you check whether conversion succeeded.
Why atoi is usually a bad choice
atoi is easy to type, and that is exactly why it is tempting. But it has a major flaw: it cannot tell you whether the conversion failed.
For example, these all become ambiguous with atoi:
- The string is actually
"0" - The string is invalid, like
"abc" - The number is out of range
- The string starts with digits and then contains garbage, like
"12xyz"
If you care about correctness, ambiguity is a problem. atoi gives you a number, but not enough information to know whether that number is trustworthy.
Use strtol or strtod instead. They give you enough context to validate the input properly.
Converting strings to integers with strtol
strtol is the most practical default for integer parsing.
Typical signature:
long strtol(const char *nptr, char **endptr, int base);
The third argument, base, controls how the parser interprets the number:
10for decimal16for hexadecimal8for octal0to let the function detect prefixes like0x
A common pattern looks like this:
#include <stdlib.h>
#include <errno.h>
#include <limits.h>
int main(void) {
const char *text = " 42";
char *end;
errno = 0;
long value = strtol(text, &end, 10);
if (end == text) {
printf("No digits were found
");
return 1;
}
if (errno == ERANGE || value < LONG_MIN || value > LONG_MAX) {
printf("Value out of range
");
return 1;
}
while (*end == ' ' || *end == ' ' || *end == '
') {
end++;
}
if (*end != '�') {
printf("Trailing characters found: %s
", end);
return 1;
}
printf("Parsed value: %ld
", value);
return 0;
}
That is more code than atoi, but it gives you a much better result. You know whether parsing succeeded, whether anything remained after the number, and whether range errors occurred.
Why the endptr matters
The endptr parameter is the main reason strtol is useful. It points to the first character that was not consumed during parsing.
This lets you distinguish between these cases:
"123"-> clean parse"123abc"-> partial parse, trailing junk present"abc"-> no digits at all
That is extremely helpful when you are reading user input, config files, CSV fields, or command-line arguments.
A simple validation pattern is:
- Call the conversion function.
- Check whether
endptr == input. - Check
errnofor range errors. - Confirm the rest of the string contains only acceptable trailing whitespace or nothing at all.
Converting strings to floating-point numbers
For decimals like 3.14, use strtod.
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
int main(void) {
const char *text = "3.14159";
char *end;
errno = 0;
double value = strtod(text, &end);
if (end == text) {
printf("No numeric input
");
return 1;
}
if (errno == ERANGE) {
printf("Floating-point overflow or underflow
");
return 1;
}
if (*end != '�') {
printf("Trailing characters: %s
", end);
return 1;
}
printf("Parsed double: %f
", value);
return 0;
}
strtod is often the right answer for:
- scientific notation like
1.2e6 - decimals with fractional parts
- precise validation of user-entered numeric values
If you need float or long double, there are specialized variants, but the validation pattern stays the same.
sscanf can work, but know the tradeoff
You can also parse numbers with sscanf.
int value;
if (sscanf(text, "%d", &value) == 1) {
/* success */
}
This is easy to read in tiny examples, and sometimes it is good enough. But sscanf is usually not the best tool for strict conversion because:
- It is less explicit about trailing characters
- Error handling is less precise than
strtol - It becomes awkward for robust validation logic
Use it when the input format is tightly controlled. For general-purpose parsing, strtol and strtod are better.
Choosing the right function
Here is a practical way to decide:
- If the input should become an integer, start with
strtol. - If the input might exceed
long, usestrtoll. - If the input is decimal, use
strtod. - If you need unsigned values, use
strtoulorstrtoull. - If you only need a quick demo and the input is trusted,
sscanfmay be acceptable.
A good rule is to choose the most specific function that still gives you enough validation.
Common mistakes to avoid
When people first convert strings to numbers in C, they often make the same mistakes.
- Ignoring trailing characters after a numeric prefix
- Not checking for range errors
- Assuming
atoican report failures - Forgetting to clear
errnobefore a conversion - Parsing user input without validating the entire string
- Using the wrong base for hex or octal input
A small amount of defensive code prevents subtle bugs later.
A reusable integer parser
If you want a clean pattern for production code, wrap strtol in a helper. This keeps validation consistent across your program.
#include <stdlib.h>
#include <errno.h>
#include <limits.h>
int parse_int(const char *text, int *out) {
char *end;
long value;
if (text == NULL || out == NULL) {
return 0;
}
errno = 0;
value = strtol(text, &end, 10);
if (end == text) {
return 0;
}
while (*end == ' ' || *end == ' ' || *end == '
') {
end++;
}
if (*end != '�') {
return 0;
}
if (errno == ERANGE || value < INT_MIN || value > INT_MAX) {
return 0;
}
*out = (int)value;
return 1;
}
This helper does a few important things:
- Rejects null pointers
- Requires at least one digit
- Rejects trailing junk
- Checks range before casting to
int
That is the kind of utility that pays off quickly in real code.
When base detection helps
Sometimes you want to let users type values in different bases. In that case, strtol with base 0 can be useful.
Examples:
123is treated as decimal0x7Bis treated as hexadecimal077may be treated as octal
This can be convenient, but it can also surprise users. If your interface expects decimal input, specify 10 explicitly. That keeps behavior predictable.
Practical recommendations
If you are writing C code today, a safe default looks like this:
- Use
strtolfor integers. - Use
strtodfor floating-point values. - Always inspect
endptr. - Always handle
errnofor range errors. - Reject strings with unexpected trailing characters.
That pattern is reliable, readable, and easy to maintain.
Final takeaway
The best way to convert a string to a number in C is not just to get a result, but to verify that the result is valid. strtol and strtod give you the control you need for real parsing, while atoi hides too much and sscanf is less precise for validation-heavy code.
If you remember the endptr and errno pattern, you can safely handle most numeric input problems in C without guesswork.