1

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, ...) {
    va_list ap;
    int i, 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);
    }
}
1
  • For what it's worth, this can all be solved and pre-calculated at compile-time using macros instead, and then we don't need to pass along a manual count of number of parameters either. Commented Nov 26 at 12:58

1 Answer 1

3

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);
    }
}
Sign up to request clarification or add additional context in comments.

4 Comments

Very nice solution and answer.
What difference does it make if I wrote int i; for(i=0; i<num; i++) instead of for(int i=0; i<num; i++) . Also for argument variable. I could have declared it before the for loop.
int i; for… leaves i visible after the for loop, whereas for (int i = 0;…) limits the scope of i. The bigger the scope of a name is, the more opportunity there is for a programmer to misuse it by accident. Sometimes it is necessary to declare a loop variable outside the loop, because its value is needed after the loop. But, if it is not, it is good practice to limit the scope. This is true for identifiers in general. Its a matter of keeping your work area neat instead of leaving things scattered all over the place. Both can work, but more mistakes happen when things are me
What difference does it make Technically: none.

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.