Search code examples
cvariadic-functions

How to write custom printf?


I have a problem. I try to make custom printf() but when I compile this code, the output doesn't seem to come as predicted.

#include <stdio.h>
#include <stdarg.h>
void print(char *, ...);

int main()
{
    char str[12]="World";
    char c='A';
    int i=100;
    print("Hello %s %c", str, c);
}

void print(char *c, ...)
{
    char *s;
    va_list lst;
    va_start(lst, c);
    while(*c!='\0')
    {
        if(*c!='%')
        {
            putchar(*c);
            c++;
            continue;
        }
        c++;
        switch(*c)
        {
        case 's': fputs(va_arg(lst, char *), stdout); break;
        case 'c': putchar(va_arg(lst, int)); break;
        }
    }    
}

Output which seem to come:Hello World Output: Hello Worlds Ac I can't figure out why 's, c' appears.


Solution

  • You aren't incrementing the pointer c after your switch case, so the while loop runs again for the characters you are using as options.

    Just add c++ after your switch case, like so:

    void print(char *c, ...)
    {
        char *s;
        va_list lst;
        va_start(lst, c);
        while(*c!='\0')
        {
            if(*c!='%')
            {
                putchar(*c);
                c++;
                continue;
            }
            c++;
            switch(*c)
            {
                case 's': fputs(va_arg(lst, char *), stdout); break;
                case 'c': putchar(va_arg(lst, int)); break;
            }
            c++;
        }
    }
    

    After making this change, I would recommend finding some way of also handling the case where the % appears at the end of the string, to avoid running into a buffer overflow. For example, before the switch, maybe check if we have reached a null terminator, and if so, break out of the loop.

    void print(char *c, ...)
    {
        char *s;
        va_list lst;
        va_start(lst, c);
        while(*c != '\0')
        {
            if(*c != '%')
            {
                putchar(*c);
                c++;
                continue;
            }
    
            c++;
    
            if(*c == '\0')
            {
                break;
            }
    
            switch(*c)
            {
                case 's': fputs(va_arg(lst, char *), stdout); break;
                case 'c': putchar(va_arg(lst, int)); break;
            }
            c++;
        }
    }