Caspian Ahlberg
Caspian Ahlberg

Reputation: 976

Why does this variadic C function not print the first argument?

I am trying to make a variadic function in C with stdarg.h, and I followed this tutorial and wrote the code exactly as he did: https://www.youtube.com/watch?v=S-ak715zIIE. I included the output in the code. I cannot figure out why the first argument is not printed, and also, why are there zeros printed at the end? I am beyond confused. Thanks!

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

void printNums(int num, ...) {
    va_list args;
    va_start(args, num);

    for (int i = 0; i < num; i++) {
        int value = va_arg(args, int);
        printf("%d: %d\n", i, value);
    }

    va_end(args);
}

int main() {
    printNums(5, 2, 3, 4);
    return 0;
}

/*
Output:
0: 2
1: 3
2: 4
3: 0
4: 0
*/

Upvotes: 0

Views: 319

Answers (1)

erik258
erik258

Reputation: 16304

va_start's first argument is the last parameter that isn't variadic. So num holds the 5, and the rest hold the variadics:

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

void printNums(int num, ...) {
    va_list args;
    va_start(args, num);

    printf("%d: %d\n", 0, num);
    for (int i = 1; i <= num; i++) {
        int value = va_arg(args, int);
        printf("%d: %d\n", i, value);
    }

    va_end(args);
}

int main() {
    printNums(5, 2, 3, 4);
    return 0;
}
0: 5
1: 2
2: 3
3: 4
4: 0
5: 0

also, why are there zeros printed at the end? I am beyond confused. Thanks!

Because of this line:

    for (int i = 1; i <= num; i++) {

You pass the value 5 as num to printNums(). In the for loop you act as though it describes the number of variadic arguments to read, but it doesn't - you passed 3 variadics, not 5. The last 2 calls to va_start therefore yield undefined behavior, since you've read past the end of valid variadic arguments. It's just mere chance that you happen to get 0 here - it could be some other random value.

Note that there is no way with mere variadic macros to know how many arguments were passed. Nor is there a way to assert their type. You can assume their type and specify their length at runtime if you wish:

$ ./t3
0: 5
1: 2
2: 3
3: 4
#include <stdio.h>
#include <stdarg.h>

void printNums(int num, ...) {
    va_list args;
    va_start(args, num);

    for (int i = 0; i < num; i++) {
        int value = va_arg(args, int);
        printf("%d: %d\n", i, value);
    }

    va_end(args);
}

int main() {
    printNums(4, 5, 2, 3, 4);
    return 0;
}

Variadic functions are primarily valuable when writing functions like printf, where unknown types and quantities of arguments are required (see the example from the man page) Using passing a list of known types would be more conveniently accomplished by passing an array and count int:

$ cat t.c
#include <stdio.h>

void printNums(int count, int* nums) {
    for (int i = 0; i < count; i++) {
        printf("%d: %d\n", i, nums[i]);
    }
}

int main() {
    int nums[] = {5,2,3,4};
    printNums(4, nums);
    return 0;
}

that just doesn't make a very good video about variadics :P

Upvotes: 4

Related Questions