Skip to content
joolibarPublic

About

A custom reimplementation of the C printf() function, built from scratch using variadic arguments. Handles every required conversion (%c %s %p %d %i %u %x %X %%) plus optional formatting flags for width, precision, and padding — with zero memory leaks

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ft_printf - Formatted Output Engine

​

Language Norminette Bonus

printf() has been called from every shell, every language, every terminal you've ever opened.
This is what happens when I stop treating it like magic.

📜 Context  •  🧩 Modules  •  🏗️ Architecture  •  🧪 Testing  •  💻 Usage


📜 The Context

"printf() looks like one function. Underneath, it's a dispatcher pretending to be simple."

ft_printf picks up where libft's fixed-parameter functions stop. Every call can carry a different number of arguments, of different types, in an order only the format string knows — and it's the parser's job to make sense of that at runtime, one % at a time.

The mission: rebuild printf from the format string up — with malloc, free, write, and the va_ macros as the only libc functions on the table. Every digit converted, every hex value formatted, every pointer address printed in hexadecimal: built from raw arithmetic, because there's nothing else to borrow it from.

🧩 The Modules

Two phases, two different problems to solve:

  1. Specifier Resolution (Mandatory): Parsing the format string and handling the nine standard conversion specifiers — the baseline every printf call assumes works.
  2. Flag & Precision Handling (Bonus): Turning the parser into a state machine that handles field width, precision, and layout flags (-0. # +) without breaking on conflicting combinations.

Click on each module below to view conversion catalogs and technical specifications.


🔹 Module I — Specifier Resolution (Mandatory)

Nine specifiers, one dispatcher — no format string breaks the loop.

⚙️ Type Conversions

Each conversion specifier extracts its argument from the variadic list via va_arg and passes it to a dedicated rendering handler — enforcing single-responsibility design and preventing cross-type side effects.

Detection order is critical: while %i and %d share output behavior, they represent distinct specifiers in the POSIX formatting standard. Furthermore, %% requires no argument extraction — misinterpreting it would advance the variadic pointer into undefined memory.

📂 View Specifier Catalog
Specifier Technical Description Expected Data Type
%c Prints a single ASCII character. int (promoted from char)
%s Prints a null-terminated string. char *
%p Prints a memory address (pointer) in hexadecimal format. void *
%d Prints a signed decimal (base 10) integer. int
%i Prints a signed integer (same behavior as %d in this context). int
%u Prints an unsigned decimal (base 10) integer. unsigned int
%x Prints an unsigned number in lowercase hexadecimal (base 16). unsigned int
%X Prints an unsigned number in uppercase hexadecimal (base 16). unsigned int
%% Prints a literal percent sign. None

🔸 Module II — Flag & Precision Handling (Bonus)

🚧 Currently in development — and by 42's own rules, it can't even be graded until the mandatory build is flawless.

Field width and precision look like formatting details. They're actually where the state machine either holds or breaks.

While mandatory conversion resolves baseline data types, this module transitions the parser into a robust finite-state machine capable of resolving intricate format combinations.

Current engineering focus:

  • 🛠️ Flag parsing: Buffering modifier flags (-, 0, ., #, +, ) prior to specifier resolution.
  • 📏 Field width & precision: Dynamically computing padding, truncation, and zero-fill boundaries.
  • 🧠 Flag precedence & conflict resolution: Gracefully handling conflicting flags (e.g., - overriding 0) according to POSIX standards.

Full flag routing tables land once this module compiles clean.


🏗️ System Architecture

The project separates string parsing from data conversion on purpose — the parser never needs to know how a hex number gets built, and the conversion functions never need to know where in the string they were called from.

🔄 The Execution Flow

  1. Parse: Scans the format string character by character.
  2. Detect: Intercepts % tokens and dispatches to the specifier resolver.
  3. Extract & Render: Retrieves the variadic argument via va_arg and formats the representation.
  4. Output & Count: Writes buffered bytes to stdout while tracking total character count for the return value.

📂 Project Structure

ft_printf/
├── inc/
│   └── ft_printf.h            # Single header, every prototype declared once
├── src/
│   ├── ft_printf.c            # Entry point and parsing loop
│   ├── ft_check_specifier.c
│   ├── ft_c.c                 # One file per specifier — no exceptions
│   ├── ft_s.c
│   ├── ft_p.c
│   ├── ft_d.c
│   ├── ft_i.c
│   ├── ft_u.c
│   ├── ft_x.c
│   └── ft_x_upper.c
├── config/
│   ├── colors.mk              # Same ANSI Makefile styling as libft
│   └── banners.mk
├── tests/
└── Makefile

🔩 Design Decisions

  • One specifier per file: Each conversion handler (ft_c.c, ft_d.c, ft_x.c, etc.) is isolated in its own compilation unit, adhering to the Single Responsibility Principle and keeping codebase modifications localized.
  • Decoupled detection and rendering: ft_check_specifier.c purely routes conversion tokens without performing formatting, ensuring parsing logic remains modular and independently testable.
  • Unified tooling with libft: Shared config/colors.mk and config/banners.mk establish a consistent build pipeline and visual interface across projects.
  • Strict return-value contract: Accurately matching printf return values is just as critical as displaying characters. Each handler tracks byte emissions to guarantee exact return-value parity.
  • Native archive generation: Built strictly using ar rcs rather than high-level tooling, adhering to standard static library construction.

🧪 Testing & Validation

A printf clone that "looks right" isn't the same as one that returns the right byte count.

Beyond Norminette compliance, this project ships with a dedicated test suite validating output against the real printf — same input, same format string, byte-for-byte comparison.

tests/
└── test_printf.c   # Validates every mandatory specifier against libc's printf

🚧 Bonus flag coverage isn't in the suite yet — it lands alongside Module II.

Running the tests

make test

The suite prints color-coded (ANSI) pass/fail output per specifier, checking output and return value against the real printf side by side.

  • Static Analysis: Validated by Norminette (42's internal linter).
  • Compiler Flags: Compiled strictly with -Wall -Wextra -Werror.

💻 Compilation & Usage

⚡ Installation

Make sure you have a C compiler (gcc or clang) and make installed, then clone the repository:

git clone https://github.com/joolibar/ft_printf.git
cd ft_printf

Compile the library:

make

🛠️ Usage

Compiling generates libftprintf.a. Include the header in your .c files:

#include "ft_printf.h"

int main(void)
{
    ft_printf("System boot: %s\n", "OK");
    ft_printf("Memory address: %p\n", &main);
    return (0);
}

Link the archive when compiling your project, pointing the compiler to inc/:

gcc -Wall -Wextra -Werror your_file.c -Ift_printf/inc -Lft_printf -lftprintf -o your_program


"Variadic parsing behind a simple interface —
dispatching types one format specifier at a time."
 🖨️✨


Capybara
Crafted by joolibar
Creative Developer building digital experiences at 42

joolibar GitHub LinkedIn Return

© 2026 joolibar  •  Validated by Moulinette 🤖, crafted by Humans 🧠.


42 ft_printf Footer

About

A custom reimplementation of the C printf() function, built from scratch using variadic arguments. Handles every required conversion (%c %s %p %d %i %u %x %X %%) plus optional formatting flags for width, precision, and padding — with zero memory leaks

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages