C Academy · Lesson

The stdarg Macros

va_list, va_start, va_arg.

Lesson 1 of 413 steps

The stdarg Macros is a free C Academy lesson on CoddyKit. This is lesson 1 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the C Academy learning path, and your progress syncs across the web and the CoddyKit app. The C Academy course includes 4 lessons in total.

What are variadic functions?

A variadic function accepts a variable number of arguments. You have used one already: printf can take any number of values.

C lets you write your own using the <stdarg.h> macros.

#include <stdio.h>

int main(void) {
    printf("%d %d %d\n", 1, 2, 3);
    printf("%s\n", "any count of args");
    return 0;
}

The ellipsis

You declare a variadic function with ... (the ellipsis) as the last parameter.

At least one named parameter must come before it, so the function knows where the variable part begins.

#include <stdio.h>
#include <stdarg.h>

void demo(int count, ...) {
    printf("This function takes %d extra args\n", count);
}

int main(void) {
    demo(3, 10, 20, 30);
    return 0;
}

Including stdarg.h

To read the variable arguments you need #include <stdarg.h>.

It defines the type va_list and the macros va_start, va_arg, and va_end.

#include <stdio.h>
#include <stdarg.h>

void show(int n, ...) {
    va_list args;
    va_start(args, n);
    va_end(args);
    printf("set up and cleaned up\n");
}

int main(void) {
    show(2, 5, 6);
    return 0;
}

va_list and va_start

Declare a va_list variable to track the current position. Then call va_start(list, last), passing the last named parameter.

This points the list at the first variable argument.

#include <stdio.h>
#include <stdarg.h>

void first(int count, ...) {
    va_list args;
    va_start(args, count);
    int value = va_arg(args, int);
    printf("first variadic arg = %d\n", value);
    va_end(args);
}

int main(void) {
    first(1, 99);
    return 0;
}

va_arg: reading values

va_arg(list, type) returns the next argument and advances the list. You must tell it the exact type to read.

Each call moves forward by one argument.

#include <stdio.h>
#include <stdarg.h>

void three(int n, ...) {
    va_list args;
    va_start(args, n);
    int a = va_arg(args, int);
    int b = va_arg(args, int);
    int c = va_arg(args, int);
    printf("%d %d %d\n", a, b, c);
    va_end(args);
}

int main(void) {
    three(3, 7, 8, 9);
    return 0;
}

va_end: cleanup

Always call va_end(list) when you finish reading. It performs any cleanup the platform needs.

Forgetting it leads to undefined behavior on some systems.

#include <stdio.h>
#include <stdarg.h>

int add_two(int dummy, ...) {
    va_list args;
    va_start(args, dummy);
    int sum = va_arg(args, int) + va_arg(args, int);
    va_end(args);
    return sum;
}

int main(void) {
    printf("%d\n", add_two(0, 4, 6));
    return 0;
}

Knowing how many arguments

The macros cannot tell you how many arguments were passed. You must convey that yourself.

A common approach is a leading count parameter, as in sum(3, a, b, c).

#include <stdio.h>
#include <stdarg.h>

int sum(int count, ...) {
    va_list args;
    va_start(args, count);
    int total = 0;
    for (int i = 0; i < count; i++)
        total += va_arg(args, int);
    va_end(args);
    return total;
}

int main(void) {
    printf("%d\n", sum(4, 1, 2, 3, 4));
    return 0;
}

Using a sentinel value

Another way to mark the end is a sentinel: a special terminating value the caller must include, such as -1 or NULL.

Loop until you read the sentinel.

#include <stdio.h>
#include <stdarg.h>

int sum_until_neg(int first, ...) {
    va_list args;
    va_start(args, first);
    int total = first, v;
    while ((v = va_arg(args, int)) != -1)
        total += v;
    va_end(args);
    return total;
}

int main(void) {
    printf("%d\n", sum_until_neg(10, 20, 30, -1));
    return 0;
}

Default argument promotions

Variadic arguments undergo default promotions: char and short become int, and float becomes double.

So always read small integers as int and floats as double with va_arg.

#include <stdio.h>
#include <stdarg.h>

void show_double(int n, ...) {
    va_list args;
    va_start(args, n);
    double d = va_arg(args, double);
    printf("%.2f\n", d);
    va_end(args);
}

int main(void) {
    show_double(1, 3.5f);
    return 0;
}

Copying a va_list

If you need to traverse the arguments twice, use va_copy(dest, src) to duplicate the list before reading.

You cannot reuse a va_list after va_end without restarting via va_start.

#include <stdio.h>
#include <stdarg.h>

void twice(int n, ...) {
    va_list a, b;
    va_start(a, n);
    va_copy(b, a);
    printf("%d %d\n", va_arg(a, int), va_arg(b, int));
    va_end(a);
    va_end(b);
}

int main(void) {
    twice(1, 42);
    return 0;
}

Computing an average

Combining the count pattern with float reading, we can average a list of numbers passed variadically.

#include <stdio.h>
#include <stdarg.h>

double average(int count, ...) {
    va_list args;
    va_start(args, count);
    double sum = 0;
    for (int i = 0; i < count; i++)
        sum += va_arg(args, double);
    va_end(args);
    return sum / count;
}

int main(void) {
    printf("%.2f\n", average(3, 2.0, 4.0, 6.0));
    return 0;
}

Quick Check

Test your understanding of the stdarg macros.

Recap

You learned the variadic mechanism:

  • Declare with ... after at least one named parameter.
  • va_start(list, last) begins, va_arg(list, type) reads, va_end(list) cleans up.
  • The macros do not know the count — use a count parameter or a sentinel.
  • Mind default promotions: read small ints as int, floats as double.
Free to start

Learn C with an AI tutor — free

Write and run real code in your browser, get instant help from a 24/7 AI tutor, and pick up where you left off on the web or in the app.

Courses
39
Lessons
144

Frequently Asked Questions

Is the “The stdarg Macros” lesson free?

Yes — the full text of “The stdarg Macros” is free to read here on the web. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the C Academy course, upgrade to CoddyKit PRO. The C Academy course includes 4 lessons in total.

What will I learn in “The stdarg Macros”?

va_list, va_start, va_arg. You practise C Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start C Academy?

No prior experience is required. C Academy on CoddyKit is structured for beginners through advanced learners, so you can start here or from the beginning and move at your own pace. This is lesson 1 of 4.

How long does the “The stdarg Macros” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this C Academy lesson?

Yes. Every C Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. The stdarg Macros
  2. Writing printf-like Functions
  3. Type Safety Concerns
  4. Practical Examples
← Back to C Academy