Introduction

The function printf() and its cousins are among of the most versatile and well-known functions for sending formatted strings as output to files and the screen. They are implemented by many languages, including C, Java, Perl, PHP, Python, Ruby, and a handful of others.

One of printf()'s signature benefits is its ability to accept a variable number of arguments. This allows printed text to contain countless variations of formatted values and strings. In this tutorial, you will learn how to extend this functionality to create your own printf() wrapper function.

Why would we want to do this? One of the most obvious reasons would be to create our own warning() or error() functions that act just like printf() but can be conditionally switched on or off by, say, compiler or command line arguments. This would save us the need to wrap individual debug statements in conditional blocks, cutting down code size and complexity.

This tutorial will demonstrate how to do this in C.

Our Silver Bullet: vprintf()

The secret to our success here is printf()'s close cousin, vprintf(). This function does the same thing as printf(), but differs in the way it accepts arguments. As we know, printf() accepts a format string and then a variable number of arguments. vprintf() on the other hand accepts a format string and a pointer to a list of arguments. It is this second argument that allows us to easily pass all of the arguments from our function into printf().

In C, we must include an additional header file to accomplish this: stdarg.h. This file contains the va_list type, which is essentially a pointer to our list of args. It also contains the functions va_start() and va_end(), which provide us access to these arguments. Below is an example function in C that takes the same arguments as printf() and simply passes them on to vprintf().

#include <stdio.h>
#include <stdarg.h>
 
void printThis( const char* format, ... ) {
    va_list args;
    va_start( args, format );
    vprintf( stderr, format, args );
    va_end( args );
}

To specify a variable number of arguments in a function, the ... keyword is used. This tells the compiler that any number of arguments may follow the format parameter.

Putting It All Together

Now that we've successfully wrapped printf() in our own function, let's go ahead and create our own error() function that behaves exactly like printf(), but prints to standard error and displays "Error: " before our messages. Just like with fprintf(), we can use vfprintf() to specify the output file (in this case, stderr) and print to it using the specified format string and arguments.

#include <stdio.h>
#include <stdarg.h>
 
void error( const char* format, ... ) {
    va_list args;
    fprintf( stderr, "Error: " );
    va_start( args, format );
    vfprintf( stderr, format, args );
    va_end( args );
    fprintf( stderr, "\n" );
}

That's all there is to it. You can call error() the same exact way you would call printf(). As I mentioned earlier, you can surround its definition with a conditional so that output will only be generated if a given flag is set. Useful, eh?

Conclusion

You should now know how to create your own wrapper function for printf() in order to customize it to suit your needs. Keep in mind that you can create functions with variable-length arguments for many other purposes as well.

You may find the following resources helpful when working with the printf() family of functions:

printf(), fprintf(), vprintf(), vfprintf()

I always welcome questions or feedback about this tutorial. Simply post a reply, I'm glad to help! Full source:

printf.c:

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

void error( const char* format, ... ) {
	va_list args;
	fprintf( stderr, "Error: " );
	va_start( args, format );
	vfprintf( stderr, format, args );
	va_end( args );
	fprintf( stderr, "\n" );
}

int main() {

	error( "An error has occured %d times due to %s", 5, "buffer overflows" );
	return 0;
}

To compile the code using gcc on *nix, extract the files and run:

	gcc -o printf printf.c

To run the test program, use the following command:

	./printf

This page was published on It was last revised on

Contributing Authors

1

1 Comment

  • Votes
  • Oldest
  • Latest
Commented
Updated

I am interested in a custom printf, that does not use stdout. I need to redirect output stream to a physical address. Any thoughts?

add a comment
0