aschepler aschepler - 29 days ago 6
C++ Question

Processing va_list piece by piece

The following program works on a 64-bit Linux machine, but crashes on a 32-bit Linux machine.

#include <cstdarg>
#include <iostream>

void proc_int(std::va_list va)
{
std::cout << va_arg(va, int);
}

void proc_str(std::va_list va)
{
std::cout << va_arg(va, const char*);
}

void outputv(std::va_list va)
{
proc_int(va);
std::cout << " ";
proc_str(va);
std::cout << "\n";
}

void output(int dummy, ...)
{
va_list va;
va_start(va, dummy);
outputv(va);
va_end(va);
}

int main()
{
output(0, 42, "hello");
}


I believe this is because
va_list
is
char*
on 32-bit but
struct __va_list_tag[1]
on 64-bit. What changes can I make to make this program portable, preferably without changing the signature of
outputv
?

Answer

From cppreference,

If a va_list instance is created, passed to another function, and used via va_arg in that function, then any subsequent use in the calling function should be preceded by a call to va_end

It's not wholly clear whether that (subsequent use) includes passing to another function, but it's certainly plausible.

Checking local (Linux) man page for comparison:

If ap [the va_list] is passed to a function that uses va_arg(ap,type) then the value of ap is undefined after the return of that function

So you're simply not allowed to pass the va_list around and use it the way you are, and the 32-bit version just happens to get away with it.

What changes can I make to make this program portable, preferably without changing the signature of outputv?

Well, just don't pass the va_list into other functions and expect it to still work afterwards:

void outputv(std::va_list va)
{
    std::cout << va_arg(va, int);
    std::cout << " ";
    std::cout << va_arg(va, const char *);
    std::cout << "\n";
}
Comments