C Programming: Developing “printf” like function

“printf” is very famous function in “C” Programming. This is the primary function we use in “C” to display something on the screen. If you are serious developer, definitely you will get a thought on developing “printf” function or you will get a question “How it was developed?”.

“printf” is a special function. Why I am using special here is, unlike other functions where fixed number of arguments are required to pass; “printf” can allow to pass any number of arguments. This is really leads to a question; how “printf” got this flexibility to accept any number of arguments?

In this article I am going to explain how we can write “printf” like functions. My intention is not to implement every option “printf” supports; but open the doors to show you how to implement those options.

Before continuing reading this article, I am strongly recommending to read this article first:

How to pass variable number of arguments to C/C++ functions?

I am assuming you are aware of using variable arguments list in “C”. If not, read above article.

Now lets look at our “printf” function first, before developing a function like “printf”.

The syntax of the function looks like below:

int printf(const char *format, ...);

The syntax of “printf” function is so simple right? Yes. It is.

The first argument tells, we need to pass constant string and the second argument (an ellipse) tells, we can pass any number of arguments.

The functionality of “printf” is purely based on its first argument. That means, “printf” changes the way it prints purely depending on its first argument.

We can “printf” first argument as “Format Specifier”. Each element in format specifier string has some meaning. For eg: format specifier “%d” to print numbers, “%s” to print strings etc.,. This article is not meant for explaining the functionality of “printf”, I am not covering all the format specifiers here.

On success “printf” function returns the number of characters written.

For simplicity, I am going to implement the functionality for “%d” and “%s” format specifiers in our “printf” like function. Lets start our implementation.

Step 1. Declare a function with the same syntax of “printf”. Give the function name you like. I am giving “myprintf” as the function name.

int myprintf (const char *format, ...);

Step 2. Our function logic is purely depending on first argument “cost char *format”. And for simplicity I am going to implement “%d” and “%s” format specifiers only.

To get list of format specifiers from first argument, we need to parse them from “format” string.

The code for this one would be like this:

   while ( format[i] != '' )
   {
       if ( ( format[i] == '%' ) && ( ( i + 1 ) < len ) )
       {
          switch ( format[i+1] )
          {
             case 'd':
                     {
                        // deal with integer value
                     }
                     break;
 
             case 's':
                     {
                        // deal with string value
                     }
                     break;
          }
       }
       i++;
   }

From above code, we are considering only “%d” and “%s” format specifiers and when they found printing the respective value.

As I mentioned beginning of the article, my intention of writing this article is not to implement exact implementation of “printf” function; but my intention is to share with you on how to implement “printf” like function. That is the reason, I kept the code simple and just implemented partial implementation for “%d” and “%s” format specifiers.

Step 3. We need to write a code to print integer values for “%d” and for “%s” we need to print associated string values. Usually we use “printf” function to print the values.

But here we are implemented “printf” like function. So, I don’t want to use “printf” function in our “printf” like function. That is the reason, we have to find out another way of displaying integer values and string values without using standard “printf” function.

We use “putchar” function to print a character on console. What about integer values? Convert each digit to an ASCII character and print the character using “putchar” function. We can use “itoa” function also.

The same way we will print each character of a string using “putchar” function.

Now we have an idea how to implement the logic. This is the time to implement the code.

Step 4. Lets put all together. The complete code is look like below:

#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <stdlib.h>
#include <limits.h>

int myprintf(const char *format, ...)
{
   if ( format == NULL )
      return 0;

   va_list valist;
   va_start(valist, format);

   int num = 0;
   char *token = NULL;
   int i = 0;
   int len = strlen(format);
   int nprinted = 0;
   int found = 0;
   
   while ( format[i] != '' )
   {
       num = 0;
       found = 0;
       token = NULL;

       if ( ( format[i] == '%' ) && ( ( i + 1 ) < len ) )
       {
          switch ( format[i+1] )
          {
             case 'd':
                     {
                        found = 1;

                        int str[40];
                        int j = 0;

                        num = va_arg(valist, int);

                        int temp = num;

			  if ( num < 0 )
                           num = -num;

                        while ( num != 0 )
                        {
                           str[j++] = (num % 10);
                           num /= 10;
                        }

                        if ( temp < 0 )
                            str[j++] = '-';
                        
                        nprinted += j;
                        j--;
                        
                        while ( j >= 0 )
                        {
                           if ( str[j] != '-' )
                              putchar(str[j--] + '0');
                           else
                              putchar(str[j--]);
                        }
                     }
                     break;
 
             case 's':
                     {
                        found = 1;

                        token = va_arg(valist, char *);
                        if ( token != NULL )
                        {
                           int j = 0;

                           while ( token[j] != '' )
                           {
                              nprinted++;
                              putchar(token[j]);
                              j++;
                           }
                        }
                     }
                     break;
          }

          if ( found != 0 )
          {
             i += 2;
             continue;
          }
       }
   
       putchar(format[i]);
       nprinted++;

       i++;
   }

   va_end(valist);

   return nprinted;
}

// 
void main()
{
   myprintf("%s : %d\n", "Hello!", 123);
}

Observe that my function name is “myprintf”. It will work well with “%d” and “%s” format specifiers. Once we compile and run the application, it will display the following message.

Hello! : 123

Finally we have successfully created a “printf” like function.

// Malin

Leave a Reply