Standard Library Overview
|
This section documents C23 (ISO/IEC 9899:2024), per ISO/IEC JTC1/SC22/WG14’s freely available working draft N3220, which WG14 documents as differing from the published standard only editorially — the reference these pages are written and verified against. This content was generated with the assistance of AI and should be verified against the WG14 draft and cppreference.com’s C reference before being relied on in production. This section’s bibliography lists the reference material consulted while preparing these pages. |
C’s standard library is small by design — around 30 headers, no containers, no networking, no filesystem traversal. What it does provide is the portable floor every C program stands on, and a set of conventions that every C API since has imitated.
The Header Catalogue
C23 defines these headers. The ones marked C23 are new in this edition; the ones marked optional may be
absent, and the corresponding __STDC_NO_*__ macro says so.
| Header | Provides |
|---|---|
|
|
|
Complex arithmetic: |
|
Character classification: |
|
|
|
Floating-point environment: rounding modes and exception flags. |
|
Floating-point limits: |
|
|
|
Alternative spellings ( |
|
Integer limits: |
|
|
|
Real maths: |
|
Non-local jumps: |
|
|
|
C11 |
|
Variadic arguments: |
|
Atomics: |
|
Bit utilities: |
|
C99 |
|
Checked integer arithmetic: |
|
|
|
Fixed-width integers: |
|
Streams and files: |
|
Allocation, conversion ( |
|
C11 |
|
Strings and memory: |
|
Type-generic maths macros over |
|
Threads: |
|
Time: |
|
Unicode: |
|
Wide strings: |
|
Wide character classification: |
C23 also removed things: the K&R-era gets (gone since C11), trigraphs, and the old
__STDC_ISO_10646__-dependent behavior. Nothing that compiled cleanly with -Wall in C17 was broken by
C23 apart from gets.
Interface Conventions
Almost every function in the library reports failure in one of four ways. Learning the pattern is more useful than memorizing individual signatures.
1. A Sentinel Return Value
malloc returns nullptr, fopen returns nullptr, fgets returns nullptr, getchar returns EOF,
strchr returns nullptr. Check the return, every time.
2. A Return Code
fclose, fseek, remove, rename, raise and the <threads.h> functions return 0/non-zero or a
named status (thrd_success).
3. errno
A global (in practice thread-local) error number set by library functions on failure. Its discipline is specific and widely got wrong:
#include <errno.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
// Rule 1: errno is only meaningful after a function DOCUMENTED to set it fails.
// A successful call may still change it.
FILE *f = fopen("/nonexistent/path", "r");
if (f == nullptr) {
// Rule 2: read errno immediately -- any intervening call may overwrite it.
int saved = errno;
fprintf(stderr, "fopen failed: %s\n", strerror(saved));
perror("fopen"); // the same message, prefixed, to stderr
}
// Rule 3: for functions that return a valid value on failure (strtol, the math
// functions), you must clear errno to zero BEFORE the call.
errno = 0;
char *end = nullptr;
long value = strtol("99999999999999999999", &end, 10);
if (errno == ERANGE) {
printf("out of range, clamped to %ld\n", value);
}
errno = 0;
double r = log(-1.0);
if (errno == EDOM) {
printf("log(-1) is a domain error, returned %g\n", r);
}
return 0;
}
Only three errno values are defined by C itself — EDOM, ERANGE and EILSEQ; everything else
(ENOENT, EACCES, …) comes from POSIX. strerror is not thread-safe in principle; strerror_r (POSIX) or
C23’s strerror with a locale-independent guarantee is the safer choice in threaded code.
Annex K — The Bounds-Checking Interfaces
C11 added an optional Annex K: strcpy_s, sprintf_s, fopen_s and friends, which take destination sizes
and call a runtime constraint handler on violation.
#include <stdio.h>
int main(void)
{
// Annex K is optional, and this is how you detect it:
#if defined(__STDC_LIB_EXT1__)
puts("Annex K bounds-checking interfaces are available");
#else
puts("no Annex K -- use snprintf and explicit sizes");
#endif
return 0;
}
The practical position: Annex K is implemented essentially only by MSVC. glibc, musl, the BSD libcs and
Apple’s libc all decline it, and WG14’s own N1969 report recommended against it. Do not build a portable
codebase on it. Use snprintf, explicit sizes, and -D_FORTIFY_SOURCE=3 instead — see
Strings and Text Processing.
Feature-Test Macros
How to ask what the implementation actually supports:
#include <stdio.h>
int main(void)
{
printf("__STDC__ = %d\n", __STDC__); // 1 for a conforming impl
printf("__STDC_VERSION__ = %ld\n", __STDC_VERSION__); // 202311L for C23
printf("__STDC_HOSTED__ = %d\n", __STDC_HOSTED__); // 1 hosted, 0 freestanding
#ifdef __STDC_NO_THREADS__
puts("no <threads.h>");
#endif
#ifdef __STDC_NO_ATOMICS__
puts("no <stdatomic.h>");
#endif
#ifdef __STDC_NO_COMPLEX__
puts("no <complex.h>");
#endif
#ifdef __STDC_NO_VLA__
puts("no variable-length arrays");
#endif
#ifdef __STDC_IEC_60559_BFP__
puts("IEEE-754 binary floating point");
#endif
#ifdef __STDC_UTF_8__
puts("char8_t literals are UTF-8");
#endif
return 0;
}
Note the distinction between a hosted and a freestanding implementation: a freestanding one (a kernel, an
MCU toolchain with -ffreestanding) is only required to provide <float.h>, <limits.h>, <stdarg.h>,
<stdbit.h>, <stdalign.h>, <stdbool.h>, <stddef.h>, <stdint.h>, <stdnoreturn.h> — no printf, no
malloc, and main need not be the entry point.
Assertions
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
static int divide(int numerator, int denominator)
{
// A programming-error check: this must never fire in a correct program.
assert(denominator != 0 && "denominator must not be zero");
return numerator / denominator;
}
static int parse_port(const char *text)
{
// An INPUT check is not an assertion -- it must survive -DNDEBUG.
if (text == nullptr) {
return -1;
}
long value = strtol(text, nullptr, 10);
if (value < 1 || value > 65535) {
return -1;
}
return (int)value;
}
int main(void)
{
printf("%d %d %d\n", divide(10, 2), parse_port("8080"), parse_port("99999"));
return 0;
}
The line to hold onto: assert is compiled out entirely when NDEBUG is defined (-DNDEBUG, which release
builds normally set), including its side effects. Never put a required operation inside an assert, and
never use assert to validate external input. Use it for invariants and preconditions your own code must
uphold.
The && "message" idiom works because a string literal is always non-null, so it prints the message with the
failed expression.
C23 also makes static_assert a keyword for compile-time checks — see
Constants, Enumerations and
Initialization.
Program Termination
| Function | Behavior |
|---|---|
|
Destroys |
|
Runs |
|
Runs |
|
Terminates immediately: no handlers, no flushing. |
|
Raises |
#include <stdio.h>
#include <stdlib.h>
static void flush_cache(void)
{
puts("2. flush_cache (registered last, runs first)");
}
static void close_log(void)
{
puts("3. close_log (registered first, runs last)");
}
int main(void)
{
if (atexit(close_log) != 0 || atexit(flush_cache) != 0) {
return EXIT_FAILURE; // registration can fail
}
puts("1. work finished");
exit(EXIT_SUCCESS); // handlers run in reverse order
}
At least 32 atexit handlers must be supported. A handler must not call exit again (undefined), and after
exit begins, calling longjmp out of a handler is undefined too.
The Environment
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
const char *home = getenv("HOME");
const char *missing = getenv("DEFINITELY_NOT_SET_12345");
printf("HOME=%s missing=%s\n",
home != nullptr ? home : "(unset)",
missing != nullptr ? missing : "(unset)");
// system(nullptr) asks whether a command processor exists at all.
if (system(nullptr) != 0) {
puts("a command processor is available");
}
return 0;
}
getenv returns a pointer to a string you must not modify or free, and which a later getenv or
setenv may invalidate — copy it if you need to keep it. There is no standard setenv (that is POSIX).
system runs a command through the shell, which makes it a command-injection hazard with any untrusted
input, and it is not thread-safe. Prefer POSIX posix_spawn/fork+execve with an argument array, which
never involves a shell.
See Also
-
Input, Output and Files —
<stdio.h>in depth. -
Strings and Text Processing —
<string.h>,<ctype.h>and conversion. -
Numbers and Math —
<math.h>,<stdbit.h>and<stdckdint.h>. -
Error Handling and Program Failure — the error-reporting strategy behind these conventions.
-
C++: Standard Library Overview — C++ layers containers, algorithms, ranges and RAII types over this same library.
References
-
WG14 N3220 — the C23 working draft (§7 "Library", §7.1.3 "Reserved identifiers", §4 para. 6 "freestanding", Annex K "Bounds-checking interfaces").
-
GCC manual — Language Standards (hosted vs. freestanding).