Skip to content
C++
Library
since C++98
Basic

<cstdlib>

C++ wrapper for C's stdlib.h, providing process control, raw memory allocation, numeric conversions, integer arithmetic, and pseudo-random numbers.

<cstdlib>since C++98

The C++ header wrapping C's <stdlib.h>, declaring process-termination functions, raw heap allocation, string-to-number conversions, pseudo-random number generation, and integer arithmetic utilities in namespace std.

Overview

<cstdlib> is not a single-purpose header. It is a heterogeneous collection that C++ inherited from C, organised into six functional groups:

GroupKey names
Process controlstd::exit, std::_Exit, std::abort, std::atexit, std::quick_exit, std::at_quick_exit
Dynamic memorystd::malloc, std::calloc, std::realloc, std::free, std::aligned_alloc
Numeric conversionsstd::strtol, std::strtod, std::atoi, std::atof, and wide-char variants
Integer arithmeticstd::abs (integral overloads), std::div, std::ldiv, std::lldiv
Pseudo-random numbersstd::rand, std::srand, RAND_MAX
Environmentstd::getenv, std::system

std::_Exit, std::quick_exit, std::at_quick_exit, and the long long variants (std::lldiv, std::lldiv_t, std::atoll, std::strtoll, std::strtoull) were formalised in C++11. std::aligned_alloc arrived in C++17.

All names live in namespace std. The deprecated <stdlib.h> form additionally injects names into the global namespace β€” avoid it in new translation units.

Syntax

cpp
#include <cstdlib>

Key constants:

cpp
EXIT_SUCCESS   // implementation-defined; conventionally 0
EXIT_FAILURE   // implementation-defined; conventionally 1
RAND_MAX       // β‰₯ 32767; maximum value returned by std::rand
MB_CUR_MAX     // maximum bytes per multibyte char in the current locale

Struct types used by div variants:

cpp
std::div_t   { int  quot; int  rem; }
std::ldiv_t  { long quot; long rem; }
std::lldiv_t { long long quot; long long rem; }  // C++11

Examples

Process termination

std::exit flushes open streams, calls std::atexit callbacks in LIFO order, destroys objects with static storage duration, then hands the status code to the OS. std::abort skips everything β€” it raises SIGABRT with no cleanup.

cpp
#include <cstdlib>
#include <iostream>

void close_database() {
    std::cout << "flushing DB connection\n";
}

int main() {
    std::atexit(close_database);   // called when main returns or std::exit fires

    bool unrecoverable = false;
    if (unrecoverable) {
        std::abort();              // SIGABRT; no destructors, no atexit callbacks
    }

    return EXIT_SUCCESS;
}

C++11 introduced std::quick_exit for multi-threaded teardown. It runs callbacks registered with std::at_quick_exit but does not run static destructors, avoiding deadlocks caused by joining threads inside destructors of global objects.

cpp
// C++11
#include <cstdlib>
#include <thread>

static std::thread worker;

void stop_worker() {
    if (worker.joinable()) worker.join();
}

int main() {
    std::at_quick_exit(stop_worker);
    worker = std::thread([]{ /* ... */ });
    std::quick_exit(EXIT_SUCCESS);   // stop_worker runs; static dtors do not
}

std::_Exit (C++11) terminates immediately with no callbacks of any kind β€” the POSIX _exit() equivalent. It is appropriate in signal handlers or after fork() before exec().

Numeric conversions

std::strtol and family report conversion errors through errno and a pointer to the first unconsumed character. The ato* family silently returns 0 on any invalid input, making them unsuitable when the caller cannot guarantee the string is valid.

cpp
#include <cstdlib>
#include <cerrno>
#include <stdexcept>

long checked_strtol(const char* s, int base = 10) {
    char* end;
    errno = 0;
    long v = std::strtol(s, &end, base);

    if (errno == ERANGE)
        throw std::overflow_error("value out of representable range");
    if (end == s)
        throw std::invalid_argument("no digits found");
    if (*end != '\0')
        throw std::invalid_argument("trailing garbage");

    return v;
}

int main() {
    long a = checked_strtol("42");         // 42
    long b = checked_strtol("0xFF", 16);   // 255
    long c = checked_strtol("-999");       // -999
}

std::strtod / std::strtof / std::strtold follow the same errno + sentinel-pointer contract for floating-point parsing.

For C++17 and later, prefer std::from_chars (<charconv>): it is locale-independent, non-allocating, carries typed error codes, and outperforms strtol in most benchmarks.

Integer division with remainder

std::div yields quotient and remainder in a single call, which compilers can map to one idiv instruction:

cpp
#include <cstdlib>
#include <iostream>

int main() {
    auto [q, r] = std::div(17, 5);              // C++17 structured bindings
    std::cout << q << " rem " << r << '\n';     // 3 rem 2

    std::lldiv_t res = std::lldiv(            // C++11
        1'000'000'007LL, 998'244'353LL);
    std::cout << res.quot << " rem " << res.rem << '\n';
}

Raw heap allocation

std::malloc and std::free are needed when interoperating with C APIs or writing low-level allocators. They bypass constructors and destructors entirely.

cpp
#include <cstdlib>
#include <new>
#include <cstring>

// Type-safe wrapper; throws std::bad_alloc on failure
template<typename T>
T* c_alloc(std::size_t n) {
    void* p = std::malloc(n * sizeof(T));
    if (!p) throw std::bad_alloc{};
    return static_cast<T*>(p);
}

int main() {
    char* buf = c_alloc<char>(256);
    std::memset(buf, 0, 256);
    std::free(buf);
}

std::aligned_alloc (C++17) provides alignment guarantees required for SIMD types and cache-line-sensitive data structures:

cpp
// C++17
#include <cstdlib>
#include <cstddef>

constexpr std::size_t cache_line = 64;

int main() {
    // size MUST be a multiple of alignment β€” UB otherwise
    void* p = std::aligned_alloc(cache_line, cache_line * 8);
    std::free(p);
}

std::realloc resizes an existing allocation. If it must move the block, the old pointer is freed automatically β€” do not free it separately:

cpp
#include <cstdlib>

int main() {
    char* p = static_cast<char*>(std::malloc(64));
    char* p2 = static_cast<char*>(std::realloc(p, 128));
    if (!p2) {
        std::free(p);   // realloc failed; p is still valid
        return 1;
    }
    // p2 is the new allocation; p is now dangling if p2 != p
    std::free(p2);
}

Pseudo-random numbers

std::rand / std::srand carry three fundamental problems: global mutable state, unspecified algorithm and period, and modulo bias when the range is not a power of two. Use them only in toy programs or legacy maintenance:

cpp
#include <cstdlib>
#include <ctime>

int main() {
    std::srand(static_cast<unsigned>(std::time(nullptr)));
    int d6 = std::rand() % 6 + 1;   // biased; avoid in new code
}

The <random> header (C++11) supersedes this entirely with std::mt19937 and std::uniform_int_distribution, which are unbiased, seedable with std::random_device, and thread-safe when used with per-thread engines.

Best Practices

  • Prefer std::from_chars over strtol/atoi (C++17): locale-independent, zero-allocation, explicit std::errc error reporting.
  • Always use EXIT_SUCCESS / EXIT_FAILURE: the actual values are implementation-defined; 0 and 1 are not portable as exit codes in all environments.
  • Register std::atexit callbacks after the resources they guard are acquired: callbacks fire in reverse registration order; a handler registered before a resource initialises will run after that resource's destructor.
  • Never mix malloc/free with new/delete: pairing them is undefined behavior.
  • Combine malloc and placement new for C++ objects in raw buffers, never pass a malloc-allocated pointer to delete.
  • Validate std::aligned_alloc inputs: size must be an exact multiple of the alignment argument; mismatches are undefined behavior.

Common Pitfalls

atoi swallows errors. std::atoi("abc") and std::atoi("") both return 0 without setting errno. On untrusted input, use std::strtol with full error checking.

std::exit does not unwind the call stack. Destructors of local variables in frames above main are not invoked. If RAII cleanup matters, return normally through the call stack or throw an exception instead.

std::atexit and std::at_quick_exit are independent registries (C++11). A function registered with one is invisible to the other termination path.

std::abs(INT_MIN) is undefined behavior. When int is 32-bit two's complement, INT_MIN has no representable positive counterpart. Guard with a range check before calling.

std::realloc failure leaves the original pointer valid. Assigning the return value of realloc directly to the original pointer leaks the old allocation when realloc returns nullptr.

Modulo bias with std::rand. rand() % N is biased when RAND_MAX + 1 is not divisible by N. The bias is small for small N but measurable in simulations. Use <random> distributions (C++11) to eliminate it.

std::exit, std::quick_exit, and std::abort

All three terminate the program, but they differ in how much cleanup they perform. std::exit(code) is the most thorough: it flushes open streams, calls every std::atexit handler in LIFO order, destroys objects with static storage duration, then returns the code to the OS. std::quick_exit(code) (C++11) calls only std::at_quick_exit handlers and skips static destructors, making it safe in multi-threaded programs where joining threads inside a global destructor would deadlock. std::abort() does none of this β€” it raises SIGABRT immediately with no cleanup whatsoever.

cpp
// C++11
#include <cstdlib>
#include <iostream>

void flush_logs() { std::cout << "[log] flushed\n"; }
void stop_metrics() { std::cout << "[metrics] stopped\n"; }

int main() {
    std::atexit(flush_logs);              // runs on std::exit or normal return
    std::at_quick_exit(stop_metrics);     // runs on std::quick_exit only

    // std::exit(EXIT_SUCCESS);   // flush_logs fires, static dtors run
    // std::quick_exit(0);        // stop_metrics fires, no static dtors
    // std::abort();              // SIGABRT β€” nothing fires
}

std::atexit handlers and std::at_quick_exit handlers are independent registries: a function registered with one is never called by the other termination path. Register cleanup handlers immediately after acquiring the resource they protect, so LIFO order unwinds acquisitions correctly.

cpp
// C++11
#include <cstdlib>

struct DB { DB() { /* open */ } ~DB() { /* close */ } };

DB* g_db = nullptr;

void shutdown_db() {
    delete g_db;  // explicit cleanup before static dtors run
    g_db = nullptr;
}

int main() {
    g_db = new DB();
    std::atexit(shutdown_db);   // called before g_db's own dtor
    std::exit(EXIT_SUCCESS);
}

std::abort is invoked internally by std::terminate (when an exception escapes a noexcept function) and by a failed assert() macro. Prefer std::exit for expected shutdown paths and reserve std::abort for truly unrecoverable situations where partial cleanup could make things worse (e.g., corrupted heap).

Memory Management: malloc, calloc, realloc, free

The C allocation functions operate on raw bytes β€” they never call constructors or destructors. This makes them inappropriate for C++ objects with non-trivial lifetimes, but essential when interfacing with C APIs that own memory, when writing custom allocators, or when you need the realloc growth semantics that operator new does not offer.

cpp
#include <cstdlib>
#include <new>
#include <cstring>

// Correct: malloc + placement new for type-erased allocators
template<typename T, typename... Args>
T* typed_alloc(Args&&... args) {
    void* raw = std::malloc(sizeof(T));
    if (!raw) throw std::bad_alloc{};
    return ::new (raw) T(std::forward<Args>(args)...);  // placement new
}

// Correct teardown: explicit dtor, then free β€” never delete on malloc memory
template<typename T>
void typed_free(T* p) {
    p->~T();
    std::free(p);
}

std::calloc(count, size) allocates count * size bytes and zero-initialises every byte, which is useful for plain-old-data buffers that must be clean without a separate memset. std::malloc leaves memory uninitialised.

cpp
#include <cstdlib>

int main() {
    // calloc: zero-initialised, suitable for C-style structs
    int* arr = static_cast<int*>(std::calloc(128, sizeof(int)));

    // malloc: uninitialised, slightly faster when you will overwrite anyway
    int* buf = static_cast<int*>(std::malloc(128 * sizeof(int)));

    std::free(arr);
    std::free(buf);
}

std::realloc is the critical pitfall: always save the original pointer before calling, because realloc returns nullptr on failure while leaving the original block intact.

cpp
#include <cstdlib>

int main() {
    char* p = static_cast<char*>(std::malloc(64));
    char* tmp = static_cast<char*>(std::realloc(p, 256));  // save to tmp first
    if (!tmp) {
        std::free(p);   // p is still valid; handle error
        return 1;
    }
    p = tmp;            // only update p after confirming success
    std::free(p);
}

Prefer operator new / new[] for all C++ objects: they call constructors, throw std::bad_alloc on failure (no null check needed), and pair correctly with delete / delete[]. Reserve std::malloc for C-interop boundaries and custom memory pools.

Numeric Conversions: atoi, atol, strtol, strtod

The ato* family (std::atoi, std::atol, std::atof) have no error-reporting mechanism whatsoever. On invalid input or overflow they return 0 (or 0.0) silently, making them safe only when the input is guaranteed valid β€” which is almost never true in production code that reads from files, network, or user input.

cpp
#include <cstdlib>

int main() {
    int a = std::atoi("42");      // 42
    int b = std::atoi("abc");     // 0 β€” silent failure
    int c = std::atoi("");        // 0 β€” silent failure
    int d = std::atoi("99999999999999999"); // UB or 0 β€” overflow not reported
}

std::strtol / std::strtoul / std::strtod expose full error information through errno and an output pointer to the first character not consumed. This makes them strictly superior to atoi for any code that handles external input.

cpp
#include <cstdlib>
#include <cerrno>
#include <stdexcept>
#include <climits>

long safe_parse(const char* s, int base = 10) {
    char* end;
    errno = 0;
    long v = std::strtol(s, &end, base);

    if (end == s)
        throw std::invalid_argument("no numeric characters");
    if (*end != '\0')
        throw std::invalid_argument("trailing garbage");
    if (errno == ERANGE || v == LONG_MAX || v == LONG_MIN)
        throw std::overflow_error("value out of range for long");

    return v;
}

int main() {
    long x = safe_parse("255", 16);   // 255 (hex)
    long y = safe_parse("-1024");     // -1024
}

For C++17 and later, std::from_chars (<charconv>) is the best choice: it is locale-independent, allocation-free, sets no global errno, returns a typed std::errc error code, and is measurably faster than strtol in benchmarks because it skips locale table lookups and whitespace scanning.

cpp
// C++17
#include <charconv>
#include <string_view>
#include <system_error>

int main() {
    std::string_view sv = "1234";
    int result = 0;
    auto [ptr, ec] = std::from_chars(sv.data(), sv.data() + sv.size(), result);
    if (ec == std::errc{}) {
        // result == 1234
    }
}

Sorting and Searching: qsort and bsearch

std::qsort is a C sorting primitive that knows nothing about C++ object semantics. It moves elements by copying raw bytes (memmove), so it is only safe for types satisfying std::is_trivially_copyable. Using it with types that have non-trivial copy/move constructors, destructors, or virtual tables is undefined behavior.

cpp
#include <cstdlib>
#include <cstdio>

int cmp_int(const void* a, const void* b) {
    int ia = *static_cast<const int*>(a);
    int ib = *static_cast<const int*>(b);
    // Avoid subtraction: ia - ib overflows for large values
    return (ia > ib) - (ia < ib);
}

int main() {
    int arr[] = {5, 3, 8, 1, 9, 2};
    std::qsort(arr, 6, sizeof(int), cmp_int);
    // arr is now {1, 2, 3, 5, 8, 9}
}

std::bsearch performs a binary search on a sorted array using the same comparator convention. It returns a void* to the found element, or nullptr if not present. The array must be sorted by the same ordering the comparator defines, or results are undefined.

cpp
#include <cstdlib>
#include <cstdio>

int main() {
    int arr[] = {1, 2, 3, 5, 8, 9};
    int key = 5;
    void* found = std::bsearch(&key, arr, 6, sizeof(int), cmp_int);
    if (found) {
        // *static_cast<int*>(found) == 5
    }
}

For C++ types, std::sort + std::lower_bound (<algorithm>) are strictly preferred: they use move semantics, work with any LessThanComparable type, and are typically faster due to inlining of the comparator lambda. Use std::qsort only at C-interop boundaries or when maintaining legacy C code that must remain C-compilable.

cpp
// C++11 β€” idiomatic C++ replacement
#include <algorithm>
#include <vector>

int main() {
    std::vector<int> v = {5, 3, 8, 1, 9, 2};
    std::sort(v.begin(), v.end());
    auto it = std::lower_bound(v.begin(), v.end(), 5);
    // *it == 5 if found
}

Environment and System Interaction

std::getenv reads a named environment variable and returns a pointer to a static internal buffer, or nullptr if the variable is not set. The returned pointer is valid only until the next call to std::getenv or std::putenv on some platforms, so copy the value immediately into a std::string if you need to keep it.

cpp
#include <cstdlib>
#include <string>
#include <stdexcept>

std::string require_env(const char* name) {
    const char* val = std::getenv(name);
    if (!val)
        throw std::runtime_error(std::string("missing env var: ") + name);
    return val;  // copy into std::string immediately
}

int main() {
    std::string home = require_env("HOME");
    std::string path = require_env("PATH");
}

std::system(cmd) passes cmd to the host shell (/bin/sh -c on POSIX, cmd.exe /c on Windows) and blocks until the subprocess completes. The return value is implementation-defined β€” on POSIX it is the value from waitpid, which can be inspected with WEXITSTATUS. Passing nullptr tests whether a shell is available; a non-zero return means a shell exists.

cpp
#include <cstdlib>
#include <cstdio>

int main() {
    if (std::system(nullptr) == 0) {
        std::fputs("no shell available\n", stderr);
        return 1;
    }

    int rc = std::system("ls -l /tmp");
    // rc is implementation-defined; on POSIX: WEXITSTATUS(rc) is ls's exit code
}

Never pass user-controlled input directly to std::system. Shell injection allows arbitrary command execution. For controlled subprocess management, use execv/execve (POSIX) or CreateProcess (Win32), which bypass the shell entirely and accept argument arrays instead of a single string. std::system is acceptable for developer tooling and build scripts where the command string is a compile-time constant.

Program-Wide Integer Division: div, ldiv, lldiv

std::div(numerator, denominator) returns both quotient and remainder in a single std::div_t struct, which a compiler can map to one integer division instruction on architectures where idiv / sdiv yields both results simultaneously. While modern compilers are often smart enough to merge a nearby a/b and a%b into a single instruction, std::div makes the intent explicit and guarantees the optimisation regardless of compiler version or optimisation level.

cpp
#include <cstdlib>
#include <iostream>

int main() {
    std::div_t r = std::div(17, 5);
    std::cout << r.quot << " rem " << r.rem << '\n';  // 3 rem 2

    // Structured bindings (C++17)
    auto [q, rem] = std::div(-23, 7);  // C++17
    std::cout << q << " rem " << rem << '\n';  // -3 rem -2
}

std::ldiv operates on long operands and returns std::ldiv_t; std::lldiv (C++11) operates on long long and returns std::lldiv_t. Use the variant that matches your operand type to avoid implicit conversion.

cpp
// C++11
#include <cstdlib>
#include <iostream>

int main() {
    std::ldiv_t a = std::ldiv(1000000L, 7L);
    std::cout << a.quot << " rem " << a.rem << '\n';

    std::lldiv_t b = std::lldiv(1'000'000'007LL, 998'244'353LL);  // C++11
    std::cout << b.quot << " rem " << b.rem << '\n';
}

std::div is particularly useful when implementing decimal arithmetic (splitting cents into dollars and cents), computing column layout (distributing pixels evenly across columns with a remainder), or writing hash functions that need both the bucket index and a secondary hash from the same division. The result for negative operands follows truncation-toward-zero semantics: std::div(-7, 2) yields {-3, -1}, not {-4, 1}.

See Also

  • <charconv> β€” std::from_chars, std::to_chars: locale-independent numeric conversions with typed error codes (C++17)
  • <random> β€” std::mt19937, std::uniform_int_distribution: correct replacement for std::rand/std::srand (C++11)
  • <new> β€” std::bad_alloc, placement new, std::nothrow: C++ allocation primitives that layer on top of raw heap operations
  • <memory> β€” std::unique_ptr, std::allocator: RAII wrappers and allocator model superseding manual malloc/free in C++ code