A custom implementation of the printf function in C, recreating the behavior of the standard library's printf with support for various format specifiers.
This project implements a custom ft_printf function that mimics the behavior of the standard printf function. It handles multiple format specifiers and returns the number of characters printed.
| Specifier | Description |
|---|---|
%c |
Single character |
%s |
String |
%d / %i |
Signed decimal integer |
%u |
Unsigned decimal integer |
%x |
Hexadecimal (lowercase) |
%X |
Hexadecimal (uppercase) |
%p |
Pointer address |
%% |
Literal percent sign |
ft_printf.c- Main printf function implementationft_printf.h- Header file with function declarationsft_print_chars.c- Character and string printing functionsft_print_numbers.c- Number printing functions (decimal, unsigned, hex)ft_print_pointer.c- Pointer printing functionsmain.c- Test file with examplesMakefile- Build configuration
To compile the library:
makeTo compile and run with the test main:
make compileInclude the header and use ft_printf just like the standard printf:
#include "ft_printf.h"
int main(void)
{
ft_printf("Hello %s! Number: %d\n", "World", 42);
return 0;
}- Handles null pointers safely (prints "(null)" for strings, "0x0" for pointers)
- Recursive implementation for number printing
- Memory-efficient with no dynamic allocation
- Returns the total number of characters printed
- Supports both lowercase and uppercase hexadecimal output
This implementation follows the 42 school coding standards.
- A variadic function takes at least one fixed argument an ellipsis(…) as the last parameter.
- The ellipsis (...) in the function parameter list is used to indicate that the function can take a variable number of arguments of any type.
- The types and names of the first N arguments are specified as usual, while the variable number of arguments are accessed using the va_list, va_start, va_arg, and va_end macros.
return_type name(fixed_arg, ...);
- This macro initializes the va_list to retrieve arguments from the variable arguments section.
va_start(list, fixed_arg);
where,
-
list: The va_list to initialize.
-
fixed_arg: The last fixed argument before the variable arguments (...).
-
This macro returns the next argument from the list. It must be used repeatedly to access all arguments.
va_arg(list, type);
- The number of times it should be called should not exceed the number of parameters passed. Due to this, the count of variable arguments passed is also passed as fixed parameters.
where,
- type: The type of the argument to retrieve.
Note: It is important to not mix up the type of the arguments.
- Once all the arguments are processed, use va_end() to clean up the va_list. This ensures that resources associated with va_list are properly released.
va_end(list);
- Creating custom variadic functions in C requires the use of four macros:
va_list,va_start,va_arg, andva_end. va_listmacro is used to declare a variable that will hold the list of arguments passed to the functionva_startmacro is used to initialize the va_list variable, and must be passed the last non-variadic argument of the function.
- va_start take args (va_list type) and fixed parameter before the variadic.
int func(int count, ...)
{
va_list args;
va_start(args, count)
}- The compiler arranges the arguments in memory (usually on the stack).
- It places the named arguments (
count) first. - Then, it places the variadic arguments (
...) immediately after the last fixed argument. va_start(args, count)tells the program:
👉 “Start reading arguments aftercount.”- When you do
va_start(args, count), the macro uses&countto find the location in memory and then steps forward to find the variadic arguments. va_start(args, last_named_param)works because arguments are stored in order.
- It retrieves the next argument from the
va_listand automatically advances the pointer to the next one, based on the size oftype.
int x = va_arg(args, int);
Means:
- “Get the next argument as an
int.” - “Move the internal pointer forward by
sizeof(int)bytes to get ready for the next one.”
How va_arg Works
va_list(usually implemented as a pointer) keeps track of the current position in the argument list.va_arg:- Dereferences that pointer, casting it to the given
type - Moves the pointer forward by the size of that type
- Dereferences that pointer, casting it to the given
- For types smaller than
int(char,short), they’re promoted tointdue to default argument promotion.- So: pass
char, read asint.
- So: pass
In C, when you pass arguments to a variadic function (a function using ...), the compiler applies default argument promotions to the variable arguments.
Because variadic functions don’t have type information for ..., the compiler promotes certain types to make them predictable and safe.
Because the variadic part (...) has no type information at compile time, the compiler needs a standard way to handle the arguments to avoid misinterpretation.
Types smaller than int are promoted to int.
floatis promoted todouble. No more rules.
- Using the wrong type in
va_arg()will cause undefined behavior, often crashing or producing garbage.