Search topics...

Have you ever implemented from scratch any functions from the C Standard Library (that ships with most compilers)? Created your own because functions in C library didn't support something you needed?

0 upvotes
Practice with AISoon

Reimplementing standard-library functions is common in embedded work. The usual technical motivations are:

  • Code size / footprint. The library version pulls in too much. The canonical example is printf: full implementations drag in float formatting, wide-char support, and locale code (kilobytes of flash). A "printf-lite" that supports only %d/%u/%x/%c/%s can be a fraction of the size. Similarly, custom memcpy/memset/strcpy avoid library bloat on tiny MCUs.
  • Missing in a freestanding / minimal runtime. In a freestanding environment (no OS, bare metal) only a small part of the standard library is guaranteed. Reduced libraries like newlib-nano or picolibc omit or stub functions, and some toolchains require you to provide your own. Heap functions in particular may be absent or require porting (_sbrk).
  • Performance. A hand-tuned memcpy/memset that uses word- or burst-sized transfers, DMA, or architecture-specific instructions can far outperform a generic byte-loop on the target.
  • Determinism / real-time. Replacing malloc/free with a fixed-block pool allocator (same API, deterministic timing) so real-time code never hits the variable-latency system heap.
  • Custom behavior / retargeting. Implementing the low-level hooks (_write, _read, _sbrk, fputc) so printf/scanf talk to a UART or RTT instead of a nonexistent console; or a memcpy variant that is safe across volatile/MMIO regions.

Common functions reimplemented in practice: memcpy, memset, memmove, strlen/strcpy/strcmp, malloc/free, and a trimmed printf/snprintf. A simple example of a size-optimized memset:

c
void *my_memset(void *dst, int c, size_t n) {
unsigned char *p = dst;
while (n--) *p++ = (unsigned char)c;
return dst;
}

(A production version would copy word-at-a-time once aligned, handling the head/tail bytes separately, for speed.) The key engineering caution is to keep the same contract as the standard function (return values, overlap semantics, NUL handling) so callers behave identically.