Variadic Functions and the Ellipsis (
...)
-
In C, the ellipsis (
...) is used in function signatures to indicate that the function can accept an arbitrary number of arguments.int ft_printf(const char *str, ...) -
The arguments passed to the function using this mechanism are called “variable arguments(va)”.
What is va_list?
At its core, va_list is a way for C functions to accept a variable number of arguments. It's like a special kind of list that holds all the extra arguments you pass to a function.
The Magical Macros
There are a few key macros that come into play when using va_list:
va_start: Think of this as "starting the list". It initialises the list to point to the first variable argument.va_arg: This is how you get the next argument from the list.va_end: This "ends the list" and cleans things up.va_copy: This is for copying the list.
All these functions are found in the stdarg.h library.
A Simple Example: Mini-printf
Let’s create a mini version of printf that only supports %d (for integers) and %s (for strings). It'll showcase va_list in action.
#include <stdarg.h>
#include <stdio.h>
void mini_printf(const char *format, ...)
{
va_list args; // Declare a va_list variable to manage the variable arguments
// Initialize the va_list 'args' to start at the argument after 'format'
va_start(args, format);
while (*format) // Loop through the format string
{
// If a format specifier is encountered
if (*format == '%')
{
format++;
if (*format == 'd')
{
// Fetch the next argument as an integer and print it
printf("%d", va_arg(args, int));
}
else if (*format == 's')
{
// Fetch the next argument as a string and print it
printf("%s", va_arg(args, char *));
}
}
else
{
// Print regular characters
putchar(*format);
}
format++; // Move to the next character
}
// Cleanup the va_list 'args' after processing
va_end(args);
}
int main(void)
{
mini_printf("Hello %s, number is %d\n", "World", 42);
return (0);
}
Notice how va_start is used after the fixed argument (format), and va_arg retrieves the arguments based on the format specifiers.
Lets dive more into each of the parameters
1. va_start: Initialising the Argument List
What it does:
va_start is the starting point. It initialises a va_list to point to the first
of the variable arguments.
How to use it:
It requires two arguments:
-
A
va_listvariable -
The last named argument before the variable arguments
Example:
va_start(args, format);
Here, args is the va_list variable, and format is the last named argument of our function.
Behind the scenes:
When you call a function, the arguments are typically placed onto a stack. va_start sets up the va_list to point to the first variable argument on this stack.
2. va_arg: Retrieving Arguments
What it does:
va_arg fetches the next argument in the list. It moves the pointer forward by the size of the type specified.
How to use it:
It requires two arguments:
-
The
va_listvariable -
The desired type of the next argument
Example:
if (*format == 'd')
{
// Fetch the next argument as an integer and print it
printf("%d", va_arg(args, int));
}
This will print the next argument as an integer.
Behind the scenes:
The macro navigates through the stack, fetching arguments based on the size of the specified type. It’s essential to specify the correct type; otherwise, you might retrieve garbage values or cause undefined behavior.
3. va_end: Cleaning Up
What it does:
va_end is the counterpart to va_start. It cleans up the memory associated with the va_list.
How Does It Work?
Without diving too deep into the weeds, here’s a simple explanation:
When you call a function, the arguments you pass in are typically placed onto a “stack” (think of it like a stack of books). With va_list, we're essentially navigating through this stack to fetch the arguments one by one.
Important Notes
-
Always have at least one fixed argument: Before the variable arguments, you should have at least one named argument. This is essential for
va_startto work. -
The type and number of arguments need clarity: Functions using
va_listdon't inherently know the type and number of variable arguments. This is why functions likeprintfneed format specifiers. -
Safety:
va_listcan be tricky. If not used correctly, it can lead to undefined behavior. Always ensure that you process the right number and type of arguments.
Links to follow me:
Also published here.
