Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How is vprintf implemented? [duplicate]

If one wants to write a function in C that passes a variable argument list through to printf one has to use the vprintf version. How can I implement this mechanism for a custom function?

In other words, how is the essence of what sets vprintf apart from printf implemented in standard conforming C?

like image 888
Toby Brull Avatar asked Sep 22 '26 13:09

Toby Brull


2 Answers

If you want to write a function which takes a va_list as an argument, the way vprintf does, then you just do that. You can extract arguments from the va_list with va_arg in the normal way.

Don't call va_start or va_end on the va_list: that's the responsibility of the caller. Since you can't restart the va_list in the normal way, if you need to scan it more than once, you'll need to va_copy it.

Here's a quick example, just for illustration (i.e. it's not meant to be the best possible implementation).

These two functions just join a bunch of strings using a provided delimiter string. The first one is the "v" version (like vsprintf), which implements the logic. The second one is the varargs version which packages up the va_list and passes it to the implementation.

The inner function runs through the arguments twice; the first time it adds the sizes of the strings. Both functions return a newly-malloc'd string which will need to be free'd by the caller.

The argument list must be terminated with a NULL.

char* vjoin(const char* delim, va_list ap) {
  va_list aq;
  va_copy(aq, ap);
  size_t dlen = strlen(delim);

  /* First pass. Use the copied va_list */
  size_t needed = 1; /* NUL terminator */
  const char* s = va_arg(aq, const char*);
  if (s) {
    needed += strlen(s);
    while ((s = va_arg(aq, const char*)))
      needed += dlen + strlen(s);
  }
  va_end(aq);

  /* Second pass. Use the original va_list */
  char* rv = malloc(needed);
  size_t offset = 0;
  *rv = 0;
  s = va_arg(ap, const char*);
  if (s) {
    strcpy(rv, s);
    offset = strlen(s);
    while ((s = va_arg(ap, const char*))) {
      strcpy(rv + offset, delim);
      strcpy(rv + offset + dlen, s);
      offset += dlen + strlen(s);
    }
  }
  return rv;
}

char* join(const char* delim, ...) {
  va_list ap;
  va_start(ap, delim);
  char* rv = vjoin(delim, ap);
  va_end(ap);
  return rv;
}
like image 125
rici Avatar answered Sep 25 '26 02:09

rici


The big difference is, of course, that printf takes a variable number of optional arguments after the format string, while vprintf takes a single non-optional argument that "points" to all arguments.

Most implementations of printf creates this single data structure and in turn calls vprintf.

Read any tutorial about variable number of arguments (also known as variadic arguments) to see how it works. This printf (and family) reference might also come in handy, as well as this variable arguments reference (which has an example).

like image 28
Some programmer dude Avatar answered Sep 25 '26 03:09

Some programmer dude