← BlogMISTAKES — 8 Jul 2026

Two Typos, Two Compiler Errors, One Variadic Function

My first submission had two separate compiler errors in four lines, and both were arguments I'd already been warned about once before. Apparently once wasn't enough.

CategoryMistakes
Published8 Jul 2026
Reading time5 min
cvariadic-functions

I'd gone through the <stdarg.h> stuff — va_list, va_start, va_arg, va_end — and felt like I basically had it down, enough to just sit down and write the function. Five lines, first submission, two compiler errors. Both of them from typing something that sounded right in my head instead of checking what the macro actually wanted.

The setup

The exercise was a max_of function: take a count of arguments, then that many ints, and return the largest.

int max_of(int count, ...) {
    va_list args;
    va_start(count, args);          /* wrong order */

    int max = va_arg(args, int);
    for (int i = 1; i < count; i++) {
        int current = va_args(args, int);   /* not a real macro */
        if (current > max) max = current;
    }

    va_end(args);
    return max;
}

I typed that out in one pass, fairly confident, and hit compile expecting maybe a missing semicolon somewhere.

What went wrong

Two errors came back, both on lines I'd written without a second thought:

error: expected '__va_list_tag *' but argument is of type 'int'
error: implicit declaration of function 'va_args'

The first one was va_start(count, args) — arguments backwards. I guess the va_list variable is supposed to go first and the last named parameter second, something about the macro needing that parameter to figure out where the variadic arguments start relative to it on the stack. I don't have a solid grip on the mechanics of why, honestly, just that it's the order it wants. I wrote count first because count was the thing I was thinking about — it felt like the main variable in the function — not because anything about the macro actually suggested that order.

The second error was almost funny because there's no ambiguity to hide behind: va_args just isn't a real macro. It's va_arg, no trailing s. I don't remember consciously deciding to add that s. It's just one of those names that sounds exactly right for "grab the next variadic argument," so I typed it fast and never looked at the line again closely enough to catch it. Looking back at it now I can't even reconstruct the moment I typed it — it's not like I debated va_arg versus va_args and picked wrong, it just came out that way, which is somehow more unsettling than if I'd actually made a bad call.

What bugs me most about both of these is that neither one was new information. I'd already had both flagged once before, in earlier notes — the va_start order thing, and the va_arg-not-va_args trap. This wasn't the first warning. It was the second, and this time it cost two compiler errors instead of just a note to myself. Apparently reading a warning once and nodding along to it isn't the same thing as actually absorbing it.

The fix

Both fixes were basically one swap each:

va_start(args, count);              /* va_list first, last named param second */
int current = va_arg(args, int);    /* va_arg, no s */

Function compiled clean after that and ran fine. Hasn't broken again on this specific piece of code, at least.

The lesson

va_start wants the list first, not the count, not whatever variable feels like the main character of the function in my head. And the extraction macro is va_arg, singular — I know that now, again, for the second time. What's actually bothering me is that both of these had already been pointed out to me once, and it didn't stick until a compiler yelled about it instead. I want to say something like "okay, now I'll always double-check argument order and never trust a macro name just because it sounds plausible" — but I already believed that after the first warning too, so I'm not totally convinced repeating the rule to myself again is what actually fixes it this time. Maybe it just has to cost me a couple more compiler errors before my hands stop doing it on autopilot.

← The Pointer I Never Touched Was Still Undefined BehaviorThe Same Precedence Mistake, Four Times, Two Months Apart →

Notes from readers

Comments — via GitHub