Search code examples
cvariadic-functions

Passing variable length argument list to another function


I have made a separate function display() for displaying the numbers and wanted to send the initialized argument pointer to this function.I am not getting the expected result. What is going wrong here? I am using gcc version 10.1.0 (GCC)

/* Expected OUTPUT */
99 + 68 -> Total = 167
11 + 79 + 32-> Total = 122
23 + 34 + 45 + 56 + 78 -> Total = 236 

/* Getting OUTPUT */
99 + 68 -> Total = 224
11 + 79 + 32 -> Total = 1410200528
23 + 34 + 45 + 56 + 78 -> Total = -1056735662


My code is :

/* Program in which the variable length list is passed to another function */
#include<stdio.h>
#include<stdarg.h>

int sum(int, ...);
void display(int,va_list);

int main(void)
{
    printf("\b\b-> Total = %d\n",sum(2,99,68));
    printf("\b\b-> Total = %d\n",sum(3,11,79,32));
    printf("\b\b-> Total = %d\n",sum(5,23,34,45,56,78));

    return 0;
}

int sum(int num, ...)
{
    int i;
    va_list ap;
    int arg,total = 0;
    va_start(ap,num);
    display(num,ap);

    for(i=0; i<num; i++)
    {
        arg=va_arg(ap,int);
        total+=arg;
    }

    va_end(ap);
    return total;
}

void display(int num,va_list ap)
{
    int i,argument;
    for(i=0; i<num; i++)
    {
        argument=va_arg(ap,int);
        printf("%d + ",argument);
    }
}

Solution

  • The rule is strict from C11 7.16p3:

    The object ap [of type va_list] may be passed as an argument to another function; if that function invokes the va_arg macro with parameter ap, the value of ap in the calling function is indeterminate and shall be passed to the va_end macro prior to any further reference to ap.

    The code:

    display(num,ap);
    va_arg(ap, ...);
    

    is invalid - once you pass va_list to another function, you can only call va_end. You could take a pointer to va_list.

    {
        ...
        display(num, &ap);
        va_arg(ap, int); // ok
        va_end(ap);
    }
    void display(int num, va_list *ap) {
        va_arg(*ap, int); // ok
    }
    

    But you are iterating va_list twice - once in display and once in sum. va_list remembers it's position, so to iterate the list twice you have to have two va_list initialized from the start. The simplest in your case is to initialize va_list twice. So you can do:

    int sum(int num, ...) {
        va_list ap;
        va_start(ap, num);
        display(num, ap);
        va_end(ap);
    
        int total = 0;
        va_start(ap, num);
        for (int i = 0; i < num; i++) {
            int arg = va_arg(ap, int);
            total += arg;
        }
        va_end(ap);
    
        return total;
    }
    
    void display(int num, va_list ap)
    {
        for(int i = 0; i < num; i++)
        {
            int argument = va_arg(ap,int);
            printf("%d + ",argument);
        }
    }