Baike.dev
All toolsOnline toolsAI codingTrendingOpen sourceNewsSubmit
Log in
< Back to tools
C

c_std

> 编程语言
Open source

Implementation of C++ standard libraries in C

1.2K stars0 likes0 views
WebsiteGitHub

About

Implementation of C++ standard libraries in C

C STL — C++ Standard Library & Python-style utilities, reimplemented in C

This project reimplements a large slice of the C++ Standard Library (containers, algorithms, smart pointers, …) together with many Python-style conveniences (statistics, random, secrets, json, regex, …) in pure C17. The goal is to give C developers familiar, well-documented building blocks — dynamic arrays, maps, strings, JSON, networking, big integers, and much more — without leaving the C ecosystem.

Highlights

  • Zero memory leaks. Every module, test suite, and README example is verified under Valgrind (--leak-check=full) — 0 leaks, 0 errors. Network/socket code is additionally checked for descriptor leaks (--track-fds=yes).

  • Cross-platform. Builds and runs on Windows (MSVC and MinGW-w64) and Linux (GCC/Clang), with POSIX/Win32 backends behind one API. Compiles cleanly under -Wall -Wextra.

  • Pure C17. No C++ — just portable standard C with thin platform shims.

  • 40+ modules. Containers, algorithms, smart pointers, strings, JSON/XML/CSV/INI, networking (TCP/UDP/HTTP), crypto & JWT, arbitrary-precision math, graphics, and more.

  • Familiar APIs. Modeled on the C++ STL (vector, map, unique_ptr, …) and the Python standard library (random, statistics, json, regex, turtle, …).

  • Heavily tested with true results. Deep per-module test suites, plus runnable examples in every README whose shown output is captured from a real run.

  • Fast & predictable. Efficient data structures (geometric vector growth with inlined push_back/at, O(1) average hashmap, O(log n) map), clear ownership rules and deallocators.

  • CMake build. Build the whole library, a single module, or one example at a time, with GCC, Clang, or MSVC.

Benchmarks vs the C++ STL

The library aims to be fast where it counts. The table below pits each C container against its C++ `` counterpart on the same workload, the same data, and the same compiler family at -O2. Both programs walk an identical deterministic key stream and compute identical checksums, so they do exactly the same work — only the container implementation differs.

Environment: Windows 10, MinGW-w64 GCC / G++ 14.2.0, -O2, single thread, best of three runs (lower is better). Absolute numbers vary by machine; the ratio is what carries over.

C container ↔ C++ STL Workload (N) C STL C++ STL C ÷ C++
vector ↔ std::vector push_back + sum, 10,000,000 0.041 s 0.031 s 1.3×
map ↔ std::map (ordered / RB-tree) insert + lookup, 1,000,000 1.00 s 1.20 s 0.83×
hashmap ↔ std::unordered_map insert + lookup, 1,000,000 0.231 s 0.364 s 0.63×
set ↔ std::set (ordered / RB-tree) insert + lookup, 1,000,000 1.17 s 1.05 s 1.1×
priority_queue ↔ std::priority_queue push + pop (heap-sort), 1,000,000 0.156 s 0.108 s 1.4×
deque ↔ std::deque push_back + iterate + pop_front, 1,000,000 0.009 s 0.004 s 2.2×
list ↔ std::list push_back + iterate + teardown, 1,000,000 0.093 s 0.075 s 1.2×
forward_list ↔ std::forward_list push_front + iterate + teardown, 1,000,000 0.080 s 0.076 s 1.05×
queue ↔ std::queue enqueue + dequeue (FIFO), 1,000,000 0.006 s 0.003 s 2.0×
stack ↔ std::stack push + pop (LIFO), 1,000,000 0.005 s 0.003 s 1.7×

C ÷ C++ is the C time divided by the C++ time — below 1.0 means the C library is faster.

  • Where C wins — map and hashmap. Both allocate their tree nodes / buckets from a pooled slab allocator, so a million inserts cost a handful of big mallocs instead of a million tiny ones, beating the STL's per-node new.
  • About set — near parity. Same pooled-slab, inline-element RB-tree as map. Lookups are actually a hair faster than std::set; inserts are a little slower, so it nets ~1.1×. Unlike map (which stores caller-owned pointers and skips the copy), set has value semantics and deep-copies each element exactly like std::set, so it can't win the copy back — and its node carries the publicly-inspectable key/left/right/parent/color fields (the test suite walks them to check the red-black invariants), which a templated std::set node doesn't expose. A large RB-tree is cache-miss-bound, so that slightly larger node is the whole gap.
  • Where C++ wins — vector and priority_queue. Both are tight loops over raw ints. The C vector now inlines push_back/at at the call site just like std::vector and closes most of the gap (~1.3×); the small residual is the type-erased ABI — a runtime itemSize stride and a size-dispatched element copy where C++ has a compile-time sizeof(int), plus the bounds check in vector_at that operator[] skips. The C priority_queue size-dispatches its heap moves the same way (~1.4×); its residual is the comparator — one indirect call through a function pointer per comparison, which std::priority_queue inlines from its template argument. Either way it's a small fixed per-element cost — the price of a uniform C API — not a worse complexity.
  • About deque. The C deque stores elements inline in byte-budgeted block buffers (one allocation per block, not one malloc per element) and inlines front / back / at with shift-indexed block math like std::deque, so it lands at ~2.2×; the residual is the out-of-line push_back / pop_front calls that std::deque inlines from templates.
  • About list. Each node stores its element inline in the same allocation (one malloc per node, like std::list) instead of a second heap block per element, which roughly halves the allocation/teardown cost and lands it at ~1.2×. The residual is a slightly larger node — the type-erased API keeps a value pointer and aligns the inline element for any type — plus the out-of-line per-node call std::list inlines.
  • About forward_list — at parity. It already uses the same single-node- allocation, inline-element design as std::forward_list, so push_front / iterate / teardown match it within measurement noise (≈1.05×). No type-erasure penalty survives here because the per-node malloc dominates and both pay it once per element.
  • About queue. The C queue is a contiguous vector used as a ring: enqueue appends and dequeue advances a front index (an O(1) bump, not a buffer shift), with the dead front prefix reclaimed in bulk when the buffer fills — so both ends are amortized O(1), like std::queue. The hot ops are inlined; the ~2× residual is the per-element copy into the contiguous buffer vs std::queue's deque node writes. (Previously dequeue shifted the whole buffer — O(n), an O(n²) drain; that's fixed.)
  • About stack. LIFO at the back of a contiguous vector, so push and pop are already O(1). push / top / pop / empty are now inlined in the header (the way std::stack inlines through its container), which cut it from ~3.3× to ~1.7×; the residual is the vector's amortized realloc-copy on growth vs std::stack's default deque adding blocks — in exchange the C stack keeps its elements fully contiguous.

Reproduce it yourself — a C and a C++ program over the identical key stream:

bash
# C  (link the containers used + algorithm, which queue depends on):
gcc -O2 bench.c vector/vector.c queue/queue.c priority_queue/priority_queue.c \
    hashmap/hashmap.c map/map.c algorithm/algorithm.c -I. -o bench_c && ./bench_c
# C++:
g++ -O2 -std=c++17 bench.cpp -o bench_cpp && ./bench_cpp

A personal note

I undertake this project out of a deep affection for the C programming language. C remains an essential tool for any computer engineer, providing the foundation needed to build efficient and robust software. This effort aims to enrich the language with the conveniences found in higher-level standard libraries.


Table of contents

  • Highlights
  • Benchmarks vs the C++ STL
  • Modules
  • Dependencies
  • Installing the dependencies
    • Windows (MSYS2 / MinGW-w64)
    • Debian / Ubuntu
    • Fedora
  • Building with CMake
    • Build everything
    • Build / run one piece at a time
    • Choosing a compiler (GCC / Clang / MSVC)
    • Build options
  • Examples
  • Per-module documentation
  • Contributing
  • License

Modules

Every module lives in its own directory with a .c source, a .h header, and a README.md containing a full API reference and runnable examples.

Containers

Module Analogous to Description
array std::array Fixed-size array wrapper with bounds-checked access.
vector std::vector Dynamic, automatically resizing array with memory pooling.
string std::string Growable string with a rich manipulation API (string/std_string.h).
list std::list Doubly linked list.
forward_list std::forward_list Singly linked list.
deque std::deque Double-ended queue with fast insertion/removal at both ends.
queue std::queue FIFO queue.
stack std::stack LIFO stack.
priority_queue std::priority_queue Heap-backed priority queue with a custom comparator.
span std::span Non-owning view over a contiguous sequence.
bitset std::bitset Fixed-size sequence of bits with bitwise operations.
map std::map Ordered associative container (Red-Black tree, O(log n)).
hashmap std::unordered_map Hash table with O(1) average insert/lookup and automatic rehashing.
set std::set Ordered, unique associative container (Red-Black tree, O(log n));
tuple std::tuple Fixed-size heterogeneous collection.
variant std::variant Type-safe tagged union with a visitor interface.
uniqueptr std::unique_ptr RAII smart pointer with automatic, scope-based cleanup.

Algorithms & numerics

Module Description
algorithm Generic algorithms inspired by ``: sort, search, transform, accumulate, …
sort Stand-alone sorting library: 8+ algorithms, benchmarking and search helpers.
statistics Mean, median, variance, … (mirrors Python's statistics).
random Pseudo-random numbers and sequence helpers (mirrors Python's random).
secrets Cryptographically secure random numbers/tokens and constant-time comparison.
uuid RFC 9562 Universally Unique Identifiers (v4/v7/v3/v5, Nil/Max), mirroring Python's uuid.
numbers Mathematical constants (header-only, like C++20 ``).
matrix Matrix creation, manipulation and linear-algebra operations.
bigint Arbitrary-precision integers (backed by GMP).
bigfloat Arbitrary-precision floating point (backed by MPFR).
evalexpr Runtime arithmetic-expression evaluator.

Text, data & I/O

Module Description
fmt Formatting / I/O library inspired by Go's fmt, with Unicode support.
encoding Base64 / Base32 / Base16 / URL encoding and decoding.
json Parse, generate and manipulate JSON.
xml Parse, create, modify and traverse XML documents.
csv Read, write and manipulate CSV files.
config Read/modify/save INI-style configuration files.
regex Regular-expression compile/match/search (PCRE on Windows, POSIX on Linux).

Issues· 0 open

View all issuesOpen on GitHub

No open issues yet, or sync has not completed.

> Tags

C

No comments yet. Be the first to share.

> Details

PublishedAug 1, 2026
UpdatedSep 17, 2026
Category编程语言
PricingOpen source

> Related tools

T
TypeScript
JavaScript 的超集,为前端与全栈提供静态类型
P
Python
通用编程语言,广泛用于 Web、数据与 AI
G
Go
Google 推出的简洁高效系统语言