<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++98The 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:
| Group | Key names |
|---|---|
| Process control | std::exit, std::_Exit, std::abort, std::atexit, std::quick_exit, std::at_quick_exit |
| Dynamic memory | std::malloc, std::calloc, std::realloc, std::free, std::aligned_alloc |
| Numeric conversions | std::strtol, std::strtod, std::atoi, std::atof, and wide-char variants |
| Integer arithmetic | std::abs (integral overloads), std::div, std::ldiv, std::lldiv |
| Pseudo-random numbers | std::rand, std::srand, RAND_MAX |
| Environment | std::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
#include <cstdlib>Key constants:
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 localeStruct types used by div variants:
std::div_t { int quot; int rem; }
std::ldiv_t { long quot; long rem; }
std::lldiv_t { long long quot; long long rem; } // C++11Examples
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.
#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.
// 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.
#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:
#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.
#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:
// 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:
#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:
#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_charsoverstrtol/atoi(C++17): locale-independent, zero-allocation, explicitstd::errcerror reporting. - Always use
EXIT_SUCCESS/EXIT_FAILURE: the actual values are implementation-defined;0and1are not portable as exit codes in all environments. - Register
std::atexitcallbacks 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/freewithnew/delete: pairing them is undefined behavior. - Combine
mallocand placement new for C++ objects in raw buffers, never pass amalloc-allocated pointer todelete. - Validate
std::aligned_allocinputs: 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.
// 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.
// 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.
#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.
#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.
#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.
#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.
#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.
// 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.
#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.
#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.
// 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.
#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.
#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.
#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.
// 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 forstd::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 manualmalloc/freein C++ code