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_list
variable -
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_list
variable -
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_start
to work. -
The type and number of arguments need clarity: Functions using
va_list
don't inherently know the type and number of variable arguments. This is why functions likeprintf
need format specifiers. -
Safety:
va_list
can 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.