Skip to main content

C/C++, Programming

C++ stoi(): Convert a String to an int

·

Illustration of a student in glasses parsing string data into integers across floating C++ UI panels

The std::stoi function converts a string that holds digits, such as "42", into an int value of 42. It lives in the <string> header, reads the leading numeric portion of the string, and throws an exception when the text is not a number or the result will not fit in an int. This guide walks through the syntax, the optional idx and base parameters, the two exceptions it throws, and the related functions like stof and from_chars, with runnable examples drawn from the same homework patterns our tutors fix on the C++ assignment help desk every week.

What std::stoi does

std::stoi turns a numeric string into an int. You hand it a std::string, it parses the integer at the front of that string, and it returns the matching int. The name reads as "string to int", which is exactly the job.

The function was added in C++11 to replace the C-era atoi. It is declared in <string>, takes a const std::string& (an overload also accepts std::wstring), and returns int. Two behaviors separate it from the old atoi: it reports failure through exceptions instead of returning a silent 0, and it accepts an optional base so a single call parses binary, octal, decimal, or hexadecimal text.

#include <iostream>
#include <string>

int main() {
    std::string text = "256";
    int value = std::stoi(text);
    std::cout << value + 1 << '\n';  // prints 257
    return 0;
}

The result value is a real int, so value + 1 does integer arithmetic and prints 257, not the string concatenation "2561" you would get if text stayed a string.

Syntax and parameters

The declaration of std::stoi takes one required argument and two optional ones:

int stoi(const std::string& str, std::size_t* idx = nullptr, int base = 10);

Each parameter controls a distinct part of the parse:

  • str: the input string. stoi skips any leading whitespace, reads an optional + or - sign, then consumes as many valid digits as it can.
  • idx: a pointer to a std::size_t. After the call, stoi writes the index of the first character it did not convert into *idx. Pass nullptr (the default) when you do not need that position.
  • base: the radix of the number, from 2 to 36. The default is 10. A base of 0 tells stoi to detect the radix from the prefix.

The return value is the parsed integer. The table below maps a few inputs to their results so the behavior is concrete.

| Call | Returns | *idx after call | Notes | | --- | --- | --- | --- | | stoi("42") | 42 | 2 | clean decimal parse | | stoi(" -17xyz", &idx) | -17 | 6 | skips spaces, reads sign, stops at x | | stoi("0x1F", nullptr, 16) | 31 | n/a | hex, 0x prefix tolerated | | stoi("1010", nullptr, 2) | 10 | n/a | binary | | stoi("755", nullptr, 0) | 755 | n/a | base 0, no prefix, treated as decimal |

The idx value is the detail most students miss. It is the only way to know where the number ended and whether anything followed it.

Convert user input into an integer

The most frequent use of std::stoi is reading a number a user typed. Console input arrives as text, so a program that does math on it converts the text to an int first.

#include <iostream>
#include <string>

int main() {
    std::string userInput;
    std::cout << "Enter a number: ";
    std::getline(std::cin, userInput);

    int userNumber = std::stoi(userInput);
    std::cout << "You entered: " << userNumber << '\n';
    std::cout << "Doubled: " << userNumber * 2 << '\n';
    return 0;
}

This version uses std::getline instead of std::cin >> userInput so the whole line is captured, including a value the user pads with spaces. stoi strips the leading whitespace for you, then userNumber * 2 runs as real arithmetic. The one risk left is bad input, which the exception-handling section below addresses.

Read numeric data from a file

std::stoi converts each line of a number file into an int as you stream the file. Files store everything as text, so a data.txt full of figures comes in as strings that need conversion before any total or comparison.

#include <fstream>
#include <iostream>
#include <string>

int main() {
    std::ifstream inputFile("data.txt");
    std::string line;
    long long sum = 0;

    while (std::getline(inputFile, line)) {
        if (line.empty()) continue;     // skip blank lines, stoi would throw on them
        int num = std::stoi(line);
        sum += num;
    }

    std::cout << "Total: " << sum << '\n';
    return 0;
}

The if (line.empty()) continue; guard matters. An empty line throws std::invalid_argument, so one stray blank row at the end of the file crashes a loop that does not check for it. Accumulating into a long long also keeps the running total from overflowing when the file holds many large values.

Parse binary, octal, and hexadecimal strings

The base parameter lets one function read any radix from 2 to 36. Pass the base as the third argument and stoi interprets the digits accordingly.

#include <iostream>
#include <string>

int main() {
    std::cout << std::stoi("101010", nullptr, 2)  << '\n';  // binary  -> 42
    std::cout << std::stoi("52",     nullptr, 8)  << '\n';  // octal   -> 42
    std::cout << std::stoi("2A",     nullptr, 16) << '\n';  // hex     -> 42
    std::cout << std::stoi("0x2A",   nullptr, 16) << '\n';  // hex     -> 42 (0x tolerated)
    std::cout << std::stoi(" z",     nullptr, 36) << '\n';  // base 36 -> 35
    return 0;
}

Base 16 tolerates a leading 0x or 0X and ignores it. Base 36 uses digits 0-9 then letters a-z, so z is 35. A special case is base 0: it makes stoi read the prefix and choose the radix itself, the same rule the C++ compiler uses for integer literals.

std::stoi("0x1F", nullptr, 0);  // 0x prefix -> hex   -> 31
std::stoi("017",  nullptr, 0);  // leading 0 -> octal -> 15
std::stoi("42",   nullptr, 0);  // no prefix -> decimal -> 42

The two exceptions stoi throws

std::stoi throws std::invalid_argument when it cannot parse a number and std::out_of_range when the number is too large for an int. Both inherit from std::exception, both live in the standard library, and both terminate the program if you do not catch them.

std::invalid_argument

This exception fires when the first non-whitespace character cannot start a number. An empty string, "abc", or " " all trigger it because stoi finds no digit to read.

#include <iostream>
#include <string>
#include <stdexcept>

int main() {
    try {
        int n = std::stoi("not a number");
        std::cout << n << '\n';
    } catch (const std::invalid_argument& e) {
        std::cout << "Invalid argument: " << e.what() << '\n';
    }
    return 0;
}

std::out_of_range

This exception fires when the digits form a valid number that exceeds the int range. On a platform where int is 32 bits, the maximum is 2147483647 (INT_MAX), so "2147483648" is one past the limit and throws.

#include <iostream>
#include <string>
#include <stdexcept>

int main() {
    try {
        int n = std::stoi("2147483648");  // INT_MAX is 2147483647
        std::cout << n << '\n';
    } catch (const std::out_of_range& e) {
        std::cout << "Out of range: " << e.what() << '\n';
    }
    return 0;
}

When the magnitude can exceed int, parse with std::stol or std::stoll into a wider type and range-check before narrowing back to int.

Catch both in one block

Production code handles both exceptions together. The order matters only for readability, since the two types are unrelated siblings, not a base and derived pair.

#include <iostream>
#include <string>
#include <stdexcept>

int parseOrZero(const std::string& s) {
    try {
        return std::stoi(s);
    } catch (const std::invalid_argument&) {
        std::cout << "\"" << s << "\" is not a number\n";
    } catch (const std::out_of_range&) {
        std::cout << "\"" << s << "\" is too big for int\n";
    }
    return 0;  // fallback when parsing fails
}

int main() {
    std::cout << parseOrZero("123")        << '\n';  // 123
    std::cout << parseOrZero("x123abc")    << '\n';  // invalid -> 0
    std::cout << parseOrZero("9999999999") << '\n';  // out of range -> 0
    return 0;
}

This is the pattern that turns a crash into a recoverable event. Many autograders feed deliberately malformed input to test exactly this defense, which is why our tutors on the C++ assignment help desk wrap every external parse in a guard like parseOrZero.

The partial-parse trap

std::stoi reads the longest valid prefix and silently ignores the rest, which means stoi("12abc") returns 12 with no error. The function only throws std::invalid_argument when the first character cannot start a number. Trailing garbage slips through.

#include <iostream>
#include <string>

int main() {
    std::cout << std::stoi("12abc") << '\n';   // 12, no exception
    std::cout << std::stoi("3.14")  << '\n';   // 3,  stops at the dot
    std::cout << std::stoi("100km") << '\n';   // 100, stops at k
    return 0;
}

To reject trailing junk, supply idx and confirm the whole string was consumed. After a clean parse, idx equals the string length.

#include <iostream>
#include <string>
#include <stdexcept>

bool parseStrict(const std::string& s, int& out) {
    std::size_t idx = 0;
    try {
        out = std::stoi(s, &idx);
    } catch (const std::exception&) {
        return false;          // invalid_argument or out_of_range
    }
    return idx == s.size();    // true only if no trailing characters remain
}

int main() {
    int n = 0;
    std::cout << parseStrict("42",    n) << " -> " << n << '\n';  // 1 -> 42
    std::cout << parseStrict("42px",  n) << " -> " << n << '\n';  // 0 (trailing px)
    return 0;
}

parseStrict is the version that grading rubrics reward. It rejects "42px" because idx lands at 2 rather than the string length of 4.

stoi and its sibling conversion functions

C++11 added a full family of string-to-number functions alongside std::stoi, each targeting a different numeric type. The table below lists every member, the type it returns, and the header that declares it.

| Function | Converts string to | Header | | --- | --- | --- | | std::stoi | int | <string> | | std::stol | long | <string> | | std::stoll | long long | <string> | | std::stoul | unsigned long | <string> | | std::stoull | unsigned long long | <string> | | std::stof | float | <string> | | std::stod | double | <string> | | std::stold | long double | <string> | | std::to_string | number to std::string | <string> | | std::from_chars | int / floats (no throw) | <charconv> |

The stol, stoll, stoul, and stoull variants share the exact (str, idx, base) signature of stoi, so the idx and base lessons above carry over without change. The floating-point trio (stof, stod, stold) drops the base argument because a radix has no meaning for a double, but keeps idx.

#include <iostream>
#include <string>

int main() {
    long long big   = std::stoll("9000000000");   // fits in long long, overflows int
    double    ratio = std::stod("3.14159");        // floating-point parse
    std::string s   = std::to_string(big);         // round-trip back to text
    std::cout << big << ' ' << ratio << ' ' << s << '\n';
    return 0;
}

The C functions atoi, atol, and strtol from <cstdlib> predate this family. atoi returns 0 on failure with no way to tell a real 0 from an error, so the C++ functions replace it. strtol does report errors but through errno and a pointer, a clumsier interface than exceptions. Treat them as legacy.

std::from_chars, the fast no-throw alternative

std::from_chars from <charconv> parses a number without throwing, without allocating, and without consulting the locale, which makes it faster than stoi for bulk parsing. It reports success through a return struct instead of an exception, so it suits hot loops and code that bans exceptions.

#include <charconv>
#include <iostream>
#include <string>

int main() {
    std::string s = "12345";
    int value = 0;

    auto [ptr, ec] = std::from_chars(s.data(), s.data() + s.size(), value);

    if (ec == std::errc{}) {
        std::cout << "Parsed: " << value << '\n';   // Parsed: 12345
    } else if (ec == std::errc::invalid_argument) {
        std::cout << "Not a number\n";
    } else if (ec == std::errc::result_out_of_range) {
        std::cout << "Too large for int\n";
    }
    return 0;
}

The returned ptr points one past the last character consumed, the same information stoi's idx gives, and ec carries the error code. Pick std::stoi when you want the shorter call and already hold a std::string. Pick std::from_chars when throughput matters or your codebase forbids exceptions. The C++ memory and resource patterns that pair well with parsing pipelines are covered in our guide to Smart Pointers in C++.

stoi quick reference

A short checklist captures the rules that keep std::stoi from biting you:

  • Include <string>, and include <stdexcept> when you catch the exceptions by their full type.
  • It throws std::invalid_argument on no-digit input and std::out_of_range when the value exceeds int.
  • It parses the longest valid prefix, so "12abc" returns 12. Use idx and compare against s.size() to reject trailing characters.
  • The base runs from 2 to 36; base 0 auto-detects from the 0x or 0 prefix.
  • It handles a leading sign and leading whitespace; it does not handle thousands separators like "1,000".
  • Reach for std::stoll for values past int, and std::from_chars for speed or a no-exception policy.

The same conversion discipline applies when you store parsed numbers in containers, which is why our Vectors in C++ walkthrough and the Templates in C++ reference both lean on validated input before pushing data into a structure.

Frequently asked questions

What header do I include to use std::stoi?

Include <string>. The std::stoi function is declared inside <string>, not <cstdlib>, even though it returns the same int type that the older atoi from <cstdlib> returns. Code that calls std::stoi without including <string> directly may still compile because another header pulls it in, but that is fragile. Include <string> yourself.

Does std::stoi throw an exception on bad input?

Yes. std::stoi throws std::invalid_argument when no conversion can be performed, such as an empty string or a string that starts with a letter. It throws std::out_of_range when the parsed number does not fit in an int, for example 2147483648 where int is 32 bits. Wrap the call in a try-catch block, or switch to std::from_chars for error reporting without exceptions.

What is the idx parameter in stoi?

The idx parameter is a pointer to a std::size_t. After the conversion, std::stoi writes the index of the first character it did not consume into that variable. For the input "42px" with idx supplied, stoi returns 42 and sets idx to 2, the position of p. The idx value lets you continue parsing the rest of the string and detect trailing garbage that stoi ignores by default.

Why does stoi("12abc") return 12 instead of throwing?

std::stoi parses the longest valid integer prefix, then stops. The string "12abc" starts with valid digits, so stoi reads 12, stops at a, and returns 12 with no error. It throws std::invalid_argument only when the first character cannot start a number. To reject trailing garbage, pass an idx pointer and confirm it equals the string length after the call.

How do I convert a binary or hexadecimal string with stoi?

Pass the base as the third argument. Use stoi(s, nullptr, 2) for binary and stoi(s, nullptr, 16) for hexadecimal. The base accepts any value from 2 to 36. Passing base 0 makes stoi auto-detect the radix from the prefix: a leading 0x means hex, a leading 0 means octal, and anything else is decimal.

What is the difference between stoi and atoi?

atoi is a C function from <cstdlib> that returns 0 on failure and gives no way to tell a real 0 from a parse error. std::stoi is the C++ replacement: it throws std::invalid_argument and std::out_of_range so failures are explicit, accepts a std::string directly, and supports an idx position and a base. Use std::stoi or std::from_chars in modern C++ and reserve atoi for legacy code.

When should I use std::from_chars instead of stoi?

Use std::from_chars from <charconv> when you parse large volumes of numbers or cannot afford exceptions. from_chars reports errors through a return code rather than throwing, ignores locale, performs no allocation, and runs faster than stoi in tight loops. Reach for std::stoi when you want the shorter call and a std::string in hand, and from_chars when performance or a no-exception policy matters.

Can stoi convert a negative number?

Yes. std::stoi reads an optional leading plus or minus sign, so stoi("-273") returns -273 and stoi("+50") returns 50. It also skips leading whitespace before the sign. If the magnitude falls below INT_MIN or above INT_MAX, stoi throws std::out_of_range rather than wrapping around.

Get help with your C++ assignment

Students bring stoi bugs to our desk most often as a crash on malformed grader input or a silent partial parse that drops a grade. Our C++ tutors have helped university students since 2014 with a 95% first-attempt pass rate, and you pay in two halves: 50% to start and 50% after you confirm the code runs on the version named in your brief. Send the prompt to the C++ assignment help team and a named developer scopes it under NDA, with pricing from $29.

cpp stoi string-conversion exception-handling std-string
Share: X / Twitter LinkedIn

Related articles

  • C/C++

    C++ Programming: A Complete Introduction

    Learn C++ from the ground up: how it compiles, its core data types, functions, and the four pillars of object-oriented programming, with runnable code.

    Mar 30, 2023

  • Case Study

    Autograder Fixed in Under 24 Hours: 100/100

    How our networking expert diagnosed a broken distance vector routing submission, fixed the output formatting bug, and delivered a 100/100 autograder score before the deadline.

    Sep 2, 2025

  • Programming

    Can You Get Caught Using Someone Else's Code?

    Yes, you can get caught. MOSS, JPlag, and Codequiry detect copied code even after renaming variables or restructuring. Here is what actually happens if you are.

    Jul 17, 2025

← All articles

Stuck on a programming assignment?

Get expert help in Java, C++, Python, JavaScript, SQL, and more. We deliver working code with a clear walkthrough so you can understand and defend it.