Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

20 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ft_printf

A from-scratch reimplementation of a subset of the C standard library's printf, built as a static library (libftprintf.a). This is the mandatory-only version — conversions only, no flags/width/precision.

Supported conversions

Conversion Meaning
%c Single character
%s String (prints (null) if given a NULL pointer, matching glibc's behavior)
%p Pointer address, in 0x... hex form
%d / %i Signed decimal integer
%u Unsigned decimal integer
%x / %X Unsigned hex, lowercase / uppercase
%% A literal %

No flags, width, or precision modifiers (-, 0, field width, .precision, #, etc.) are implemented — only the bare conversions above.

Build

make          # -> libftprintf.a
make clean    # remove object files
make fclean   # remove object files and libftprintf.a
make re       # fclean + all

Usage

#include "ft_printf.h"

int main(void)
{
    ft_printf("Hello, %s! You are %d years old (%p).\n", "world", 42, (void *)&main);
    return (0);
}
cc -Wall -Wextra -Werror your_file.c -I/path/to/ft_printf -L/path/to/ft_printf -lftprintf -o your_program

Project structure

.
├── Makefile
├── ft_printf.h          # Public prototype + internal helper prototypes
├── ft_printf.c           # Main loop: walks the format string, dispatches on '%'
├── ft_printffunc.c        # Per-conversion printing (string, hex, pointer, unsigned)
└── ft_printf_utils.c        # ft_strlen, ft_itoa (handles INT_MIN correctly)

License

MIT


Concept notes

Study notes on variadic functions, written while working through this project.

Variadic functions

What's called a variadic function relies on va_start, va_arg, va_end, and va_copy. These all operate on a variable of type va_list.

(the original notes had this as "va_cpy" — the actual standard name is va_copy)

va_list

va_list is the type used by functions that take an unspecified number of arguments of unspecified type.

unsigned int gp_offset;
unsigned int fp_offset;
void *overflow_arg_area;
void *reg_save_area;

On x86-64 System V (the common Linux/macOS ABI), va_list is implemented as a struct shaped roughly like the above — the exact layout is architecture-specific.

reg_save_area

The register save area is where values temporarily assigned to CPU registers get stored during a function call. Only a limited number of registers are available at once, and how many depends on the architecture.

overflow_arg_area

The overflow argument area holds whatever arguments didn't fit into the register save area.

Both of these areas are normally managed behind the scenes by the C compiler — reading a value that's sitting in a register is faster and simpler than always going through memory, which seems to be the main reason they exist.

Offset — a distance indicator: the integer number of bytes from an object's starting point to some later point.

gp_offset (general-purpose offset)

General-purpose registers (GPRs) are used for general-purpose data types (char, int, short, long, addresses, ...). Each register is 8 bytes.

fp_offset (floating-point offset)

Floating-point registers (FPRs) are dedicated to floating-point numbers. Each one is 16 bytes.

va_start

va_start initializes the va_list right before you start reading the variable arguments. Since the arguments live on the stack, this setup reaches into that stack frame: it computes the offset between the last named argument and the first variadic one, points the va_list at that first argument, and the compiler may perform some additional compiler-specific setup on top of that.

va_arg

va_arg takes two things — the va_list and a type — and steps past one argument at a time. Since it already holds the current address, it uses the type it's given to work out where that argument ends, and reads it accordingly.

va_end

va_end invalidates the va_list (runs it through cleanup) — it would need to be re-initialized with va_start before being used again. Being an internal data structure, cleanup mainly means resetting its internal pointers/flags; the compiler may also do its own compiler-specific teardown on top of that, possibly resetting whatever stack- or register-related state the va_list had been using — though the precise mechanism there is implementation-defined and not something the C standard spells out in detail.

About

A from-scratch reimplementation of a subset of the C standard library's printf.

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages