stdarg(3)
โจ stdarg โ variable argument lists โจ
okay bestie, so this is the manual page for stdarg(3), from the Library Functions Manual โ and if you've ever written a function like printf() that just... takes however many args you feel like, this is the whole entire magic behind it ๐
๐ NAME ๐
stdarg, va_start, va_arg, va_end, va_copy โ variable argument lists
๐ LIBRARY ๐
Standard C library (libc, -lc)
๐ธ SYNOPSIS ๐ธ
#include <stdarg.h>
void va_start(va_list ap, last);
type va_arg(va_list ap, type);
void va_end(va_list ap);
void va_copy(va_list dest, va_list src);
๐ DESCRIPTION ๐
So like, a function can be called with a varying number of arguments of varying types. iconic behaviour, honestly. The include file <stdarg.h> declares a type va_list and defines three macros for stepping through a list of arguments whose number and types are not known to the called function. mystery! suspense! ๐ต๏ธโโ๏ธ
The called function must declare an object of type va_list which is used by the macros va_start(), va_arg(), and va_end(). no va_list, no vibes, periodt.
๐ซถ va_start() ๐ซถ
The va_start() macro initializes ap for subsequent use by va_arg() and va_end(), and must be called first. it's giving opening credits, it's giving no skipping ahead.
The argument last is the name of the last argument before the variable argument list โ that is, the last argument of which the calling function knows the type. so basically the final one that isn't a surprise ๐
Because the address of this argument may be used in the va_start() macro, it should not be declared as a register variable, or as a function or an array type. just don't do it bestie.
๐ฆ va_arg() ๐ฆ
The va_arg() macro expands to an expression that has the type and value of the next argument in the call. The argument ap is the va_list ap initialized by va_start(). Each call to va_arg() modifies ap so that the next call returns the next argument. The argument type is a type name specified so that the type of a pointer to an object that has the specified type can be obtained simply by adding a * to type. cute, right?
The first use of the va_arg() macro after that of the va_start() macro returns the argument after last. Successive invocations return the values of the remaining arguments. one by one by one, slay ๐
If there is no next argument, or if type is not compatible with the type of the actual next argument (as promoted according to the default argument promotions), random errors will occur. not a vibe. not a slay. actual chaos.
If ap is passed to a function that uses va_arg(ap,type), then the value of ap is undefined after the return of that function. bye bye ap ๐
๐ง va_end() ๐ง
Each invocation of va_start() must be matched by a corresponding invocation of va_end() in the same function. matching set, bestie ๐ After the call va_end(ap) the variable ap is undefined.
Multiple traversals of the list, each bracketed by va_start() and va_end() are possible. va_end() may be a macro or a function. either way, she's flexible โจ
๐ va_copy() ๐
The va_copy() macro copies the (previously initialized) variable argument list src to dest. The behavior is as if va_start() were applied to dest with the same last argument, followed by the same number of va_arg() invocations that was used to reach the current state of src. it's a clone, it's a dupe, it's the same girl different font ๐
An obvious implementation would have a va_list be a pointer to the stack frame of the variadic function. In such a setup (by far the most common) there seems nothing against an assignment:
va_list aq = ap;
Unfortunately ๐ญ there are also systems that make it an array of pointers (of length 1), and there one needs:
va_list aq;
*aq = *ap;
Finally, on systems where arguments are passed in registers, it may be necessary for va_start() to allocate memory, store the arguments there, and also an indication of which argument is next, so that va_arg() can step through the list. Now va_end() can free the allocated memory again. To accommodate this situation, C99 adds a macro va_copy(), so that the above assignment can be replaced by:
va_list aq;
va_copy(aq, ap);
...
va_end(aq);
Each invocation of va_copy() must be matched by a corresponding invocation of va_end() in the same function. (yes, same rule as va_start(), she's not special, she's just built different.)
Some systems that do not supply va_copy() have __va_copy instead, since that was the name used in the draft proposal. throwback name, honestly.
๐ ATTRIBUTES ๐
For an explanation of the terms used in this section, see attributes(7).
va_start(),va_end(),va_copy()โ Thread safety: MT-Safeva_arg()โ Thread safety: MT-Safe race:ap
(yeah va_arg() is racing on ap, she's a little chaotic, we still love her ๐ซถ)
โญ STANDARDS โญ
C11, POSIX.1-2008.
๐ HISTORY ๐
va_start(),va_arg(),va_end()โ C89, POSIX.1-2001.va_copy()โ C99, POSIX.1-2001.
๐ CAVEATS ๐
Unlike the historical varargs macros, the stdarg macros do not permit programmers to code a function with no fixed arguments. This problem generates work mainly when converting varargs code to stdarg code, but it also creates difficulties for variadic functions that wish to pass all of their arguments on to a function that takes a va_list argument, such as vfprintf(3). so annoying, so real, but we cope ๐
๐ธ EXAMPLES ๐ธ
The function foo takes a string of format characters and prints out the argument associated with each format character based on the type.
#include <stdio.h>
#include <stdarg.h>
void
foo(char *fmt, ...) /* '...' is C syntax for a variadic function */
{
va_list ap;
int d;
char c;
char *s;
va_start(ap, fmt);
while (*fmt)
switch (*fmt++) {
case 's': /* string */
s = va_arg(ap, char *);
printf("string %s\n", s);
break;
case 'd': /* int */
d = va_arg(ap, int);
printf("int %d\n", d);
break;
case 'c': /* char */
/* need a cast here since va_arg only
takes fully promoted types */
c = (char) va_arg(ap, int);
printf("char %c\n", c);
break;
}
va_end(ap);
}
main character function, honestly ๐
๐ SEE ALSO ๐
vprintf(3), vscanf(3), vsyslog(3)
okay that's the whole page bestie, go write something variadic and unhinged, ily ๐โจ
this page is from Linux man-pages 6.20, dated 2025-05-17 โ yeah, freshly dated, the girls are keeping it current ๐๐ธ
๐ฆ where this page comes from
- arch rolling: man-pages 6.20-1 ยท BSD-2-Clause AND BSD-3-Clause AND BSD-4.3TAHOE AND BSD-4-Clause-UC AND GPL-1.0-or-later AND GPL-2.0-only AND GPL-2.0-or-later AND GPL-3.0-or-later AND LGPL-3.0-linking-exception AND LGPL-3.0-or-later AND LicenseRef-Public-Domain AND LicenseRef-UltraPermissive AND Linux-man-pages-1-para AND Linux-man-pages-copyleft AND Linux-man-pages-copyleft-2-para AND Linux-man-pages-copyleft-var AND MIT AND Spencer-94
- nixpkgs unstable: man-pages 6.20
- ๐ the page's license: BSD-4-Clause-UC
- Copyright 1990-1991, The Regents of the University of California
- ๐ girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms