Variable argument lists with ANSI C

As a rule, all functions in C must be explicitly defined: that is, the code must indicate the type of the value returned by the function (if any), and the number and types of any variables passed to the function. There are, however, exceptions to this rule, the most notable of which (in my opinion) is the printf function.

printf("Howdy!\n");
printf("Program '%s' has %d arguments\n", argv[1], argc);
printf("Locus %s[%lu, %lu] has %lu genes\n", seqid, start, end, numgenes);

Here, the printf function is being called 3 different times, with 3 completely different parameter profiles, and yet it compiles and runs correctly.

Today I was looking at how I can have similar functionality in functions that I define. This led me to the stdarg.h library (part of the C standard library). Using stdarg.h, you can define variable parameter list functions using the ... token. Then, you can use the va_list data type to refer to a variable-length argument list, and step through those arguments using the va_start and va_end functions. Below is a simplified example of one of the ways I have used this functionality in my current project.

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

void print_error_message(char *format, ...)
{
  fputs("[Error] message: ", stderr);
  va_list ap;
  va_start(ap, format);
  vfprintf(stderr, format, ap);
  va_end(ap);
  fputs("\n", stderr);
}

int main(int argc, const char **argv)
{
  print_error_message("you gave the program '%s' %d arguments", argv[0], argc - 1);
  return 1;
}

I can compile and run the example program like so.

standage@iMint ~ $ gcc -Wall -o test test.c 
standage@iMint ~ $ ./test 
[Error] message: you gave the program './test' 0 arguments
standage@iMint ~ $ ./test 1 2 3
[Error] message: you gave the program './test' 3 arguments
standage@iMint ~ $

If you wanted to write the message to a string (for later user) instead of printing it directly to a file handle, something like this would work.

void print_error_message_to_string(char *message, char *format, ...)
{
  va_list ap;
  va_start(ap, format);
  vsnprintf(message, sizeof(message), format, ap);
  va_end(ap);
}
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s