Vlad Markushin
Vlad Markushin

Reputation: 463

vfprintf doesn't work properly

I have a class:

FILE *logFile = fopen("out.log", "w");
class Log {
public:
    static void d(const char *message, ...) __attribute__((format (printf, 1, 2)));
}

In source file:

void Log::d(const char *message, ...) {
    va_list argptr;
    va_start(argptr, message);

    vprintf(message, argptr);
    printf("\n");
    fflush(stdout);
    if (logFile) {
        vfprintf(logFile, message, argptr);
        fprintf(logFile, "\n");
        fflush(logFile);
    }

    va_end(argptr);
}

But when I call for example Log::d("test %d %s %f", 10, "str", 0.1); it prints test 0 @WAíõ 0,000000 to the file.

What's wrong?

Upvotes: 2

Views: 1907

Answers (1)

Andre Kampling
Andre Kampling

Reputation: 5630

The problem is that you are using your va_list argptr 2 times. Once in the vprintf and secondly in the vfprintf call. How a va_list works with the stack is implementation defined, see here. The va_list type for example is implemented in Linux x86_64 like said here on SO:

The va_list Type

The va_list type is an array containing a single element of one structure containing the necessary information to implement the va_arg macro. The C definition of va_list type is given in figure 3.34

// Figure 3.34
typedef struct {
   unsigned int gp_offset;
   unsigned int fp_offset;
   void *overflow_arg_area;
   void *reg_save_area;
} va_list[1];

If you pass your va_list into the first vprintf call and the function returns the va_list will no longer be as before the call. The second call to vfprintf will get a "wrong/consumed va_list" then.

Solution:

Either use va_copy after va_start or use va_start 2 times if your compiler doesn't support va_copy. But remember each call to va_copy or va_start needs a corresponding call to va_end.

Upvotes: 3

Related Questions