Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C Is it possible for a function taking va_list to use it more than once?

In the program below, the variadic function process_message passes its variable arguments via the va_list argp parameter to print_message. argp is in turn passed to vsnprintf which calculates the length of the format string.

However, if argp is passed to a another function called from within print_message such as vprintf(fmt, argp), it produces nonsense output. Is it possible for a function taking va_list parameter to use it more than once?

#include <stdio.h>
#include <stdarg.h>

void process_message(const char *fmt, ...);
void print_message(const char *fmt, va_list argp);

int main(void) {
  process_message("%s:%d\n", "test message", 1);
  return 0;
}

void process_message(const char *fmt, ...) {
  va_list argp;
  va_start(argp, fmt);
  print_message(fmt, argp);
  va_end(argp);
}

void print_message(const char *fmt, va_list argp) {
  /*Calculate and print the length of the format string*/
  int len = vsnprintf(NULL, 0, fmt, argp);
  printf("Length of format string = %d\n", len);
  /*Print the rendered format string - this produces a nonsense output
   *if argp was aleady passed to another function earlier */
  vprintf(fmt, argp);
}
like image 434
user2309803 Avatar asked Jan 28 '23 11:01

user2309803


1 Answers

You can use va_copy:

The va_copy() macro copies the (previously initialized) variable argument list src to dest. The behavior is as if va_start() were applied to dest with the same last argument, followed by the same number of va_arg() invocations that was used to reach the current state of src.

You print_message() could be:

void print_message(const char *fmt, va_list argp) {
  /*Calculate and print the length of the format string*/ 
  va_list argp_copy;
  va_copy(argp_copy, argp);
  int len = vsnprintf(NULL, 0, fmt, argp_copy);
  va_end(argp_copy);

  printf("Length of format string = %d\n", len);
  /*Print the rendered format string - this produces a nonsense output
   *if argp was aleady passed to another function earlier */
  vprintf(fmt, argp);
}

NOTE:

Don't forget to call va_end after using a copied va_list.

C11:

Each invocation of the va_start and va_copy macros shall be matched by a corresponding invocation of the va_end macro in the same function.

like image 158
llllllllll Avatar answered Apr 06 '23 01:04

llllllllll