0

I'm studying the C programming language and one thing I found very interesting is the implementation of a variadic function. I'm currently trying to print each value passed into the function, but I'm only getting a part of the arguments.

I tried running some examples I found online for average number from parameters and also a sum algorithm (shown below).

#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++) {
        int num = va_arg(args, int);
        total += num;
        printf("Value #%d: %d\n", i, num);
    }

    va_end(args);

    return total;
}

int main()
{
    int result = sum(1, 2, 3, 4, 5);
    printf("The result is: %d\n", result);
}

The code above only prints:

Value #0: 2
The result is: 2

I think it's because the for loop is using the first argument as the max value of the index. But...

My question here is how printf works if it's not necessary to pass the amount of arguments to replace within the formatted string? Is it because behind the scenes, the C runtime counts how many format specifiers are declared in the formatted string? That's my guess.

2 Answers 2

4

There's no way to find out how many arguments were actually supplied.

printf() figures it out from the format string. Each of the % operators in the format string corresponds to an argument (this is a simplification), it processes as many arguments as it needs to fill each of them in.

So if you write:

printf("%d %s\n", intvar, stringvar);

it knows that there must be 2 additional arguments: one for %d and another for %s.

Another method used by some functions is a sentinel value to indicate the last argument.

execl("program", "arg0", "arg1", "arg2", (char *)NULL);

execl() processes arguments until it gets to the NULL value.

Sign up to request clarification or add additional context in comments.

5 Comments

BTW, useful and proper cast of NULL to insure a char* is passed. UV
Interesting, execl()'s approach seems promising, but how could I check inside a loop if the value I'm getting from using va_arg is a NULL value? Should I compare it with '\0'?
And though printf() only knows how many parameters there are by looking at the format string, the compiler knows exactly how many parameters were passed, so it can do the kind of inspection and checking that software cannot. If you write your own variadic functions, it's really wise to augment the external declarations with compiler-specific decorations that allow the compiler to do checking for you. GNU uses variants of __attribute__() to really good effect.
@CatBrownie "how could I check inside a loop if the value I'm getting from using va_arg is a NULL value?" by comparing it to NULL. Be aware, that execl() by definition should get passed pointers only.
@CatBrownie char *next_arg = va_arg(args, char *); if (next_arg == NULL) ...
0

If you call your variadic function as:

int result = sum(5 /*count*/, 1, 2, 3, 4, 5);

by adding the initial 5 (a count), it will do what you expect it to, but for fun try calling it with a bigger number (say, 6 or 10) and see what happens. They are easy to get wrong.

Almost the only good case for variadic functions is for variants of printf tailored for your application. My longtime favorite is die() that takes a printf-style format string (and arguments), sends it to the standard error, appends a newline, then exits the program.

#include <stdlib.h>
#include <stdarg.h>

void die(const char *format, ...)
{
    va_list args;

    va_start(args, format);
    vprintf(stderr, format, args);
    va_end(args);
    fprintf(stderr, "\n");

    exit(EXIT_FAILURE);
}

and then put this in your header files to use it:

extern void die(const char *format, ...)
   __attribute__((noexit))         // function never exits
   __attribute__((printf(1, 2)));  // looks like printf, format is arg1

Now you can call die("Program failed because %s", reason); and it bails on the program without a lot of muss and fuss. And because of the __attribute__ use, the compiler (GNU, at least) knows how to verify the parameters to the format string.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.