Question

I'm reading about how to pass optional arguments to function. But I'm unable to understand those. When I see examples, they are confusing and a bit complex. So I just started with a very simple program with what I have understood up to now.

The below program just prints the variables.

void print(int x, ...)
{
        va_list ap;
        int i = 4;     // I know I'm passing only 4 opt variables. 
        int num;

        va_start(ap, x);
        while(i--) {         // How to know how many variables came in real time?
                num = va_arg(ap, int);
                printf("%d\n", num);
        }
        va_end(ap);
        return;
}

int main()
{
        print(1,2,3,4,5);
        return 0;

}

I don't know above program is right or not. But it's working. When I change the i value to 5 printing garbage. How to know how many arguments I got (like argc in main)?

Was it helpful?

Solution 2

You can take a look to NARGS macro

Adapted to your code:

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

#define NARGS_SEQ(_1,_2,_3,_4,_5,_6,_7,_8,_9,N,...) N
#define NARGS(...) NARGS_SEQ(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1)

#define print(...) fnprint(NARGS(__VA_ARGS__), __VA_ARGS__)

void fnprint(int n, ...)
{
        va_list ap;
        int num;

        va_start(ap, n);
        while (n--) {
                num = va_arg(ap, int);
                printf("%d\n", num);
        }
        va_end(ap);
        return;
}

int main(void)
{
        print(1, 2, 3, 4, 5);
        return 0;
}

EDIT: If you want to use the same name for macro and function, use () to stop the preprocessor from expanding the function definition:

#define print(...) print(NARGS(__VA_ARGS__), __VA_ARGS__)

void (print)(int n, ...) /* Note () around the function name */
{
  ...

EDIT 2: Another (ugly) method (but without restriction in the number of args) using compound literals (std 99) and sizeof:

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

#define print(...) print(sizeof((int []) {__VA_ARGS__}) / sizeof(int), __VA_ARGS__)

void (print)(int n, ...)
{
    va_list ap;
    int num;

    va_start(ap, n);
    while (n--) {
        num = va_arg(ap, int);
        printf("%d\n", num);
    }
    va_end(ap);
    return;
}

int main(void)
{
    print(1, 2, 3, 4, 5);
    return 0;
}

print is expanded to:

print(sizeof((int []) {1, 2, 3, 4, 5}) / sizeof(int), 1, 2, 3, 4, 5);

But constructions like print(1, 2, 3, a_var++, 4, 5); or print(1, 2, some_func_returning_int(), 4, 5); evaluates a_var++ and some_func_returning_int() two times, thats a big problem.

OTHER TIPS

There is no way of knowing how many arguments are passed from inside a variable-argument function, that's why functions such as printf are using special format strings that tells the function how many arguments to expect.

Another way is of course to pass the number of "extra" arguments as the first argument, like

print(4, 1, 2, 3, 4);

Or to have a special value that can't be in the list as last argument, like

print(1, 2, 3, 4, -1);

You also have to take note that the last non-va argument you pass to the function (the argument named num in your case) is not included in the va_list, so in your case with the shown code with 4 as hardcoded number of arguments you will still print garbage, as you pass 1 for the num argument and then three va_list arguments.

Also take care because you use num as both argument and a local variable name.

Another ugly way for both int and strings:

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

#define print(...) fnprint("{" # __VA_ARGS__ )

fnprint(char *b) {
    int count = 0, i;
    if (b[1] != '\0') {
        for (i =2; b[i]; i++) {
            if (b[i] == ',')
               count++;
        }
        count++;
    }
    printf("\ncount is %i\n", count);
}

int main(void)
{
        print();
        print("1", "2");
        print(1, 2, 3, 4, 5);
        return 0;
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top