17

Actually I am trying to write my own printf() in C by using varags. But I am not getting the correct solution for this. Can anyone help me out?

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Rajesh M
  • 179
  • 1
  • 1
  • 3

8 Answers8

21

Before implementation of printf( ) function we have to deal with unusual problem which is variable arguments. As we know that printf can take many arguments besides string. So we have to use a standard library called stdarg.h to handle this variable argument problem. In this implementation context, we don’t need learn whole stdarg.h library because we use some macro functions of these library which is understandable directly by our C program.

Here is the code source which explain nice and fast

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

void Myprintf(char *,...);              //Our printf function
char* convert(unsigned int, int);       //Convert integer number into octal, hex, etc.


int main() 
{ 
    Myprintf(" WWW.FIRMCODES.COM \n %d", 9); 

    return 0;
} 


void Myprintf(char* format,...) 
{ 
    char *traverse; 
    unsigned int i; 
    char *s; 

    //Module 1: Initializing Myprintf's arguments 
    va_list arg; 
    va_start(arg, format); 

    for(traverse = format; *traverse != '\0'; traverse++) 
    { 
        while( *traverse != '%' ) 
        { 
            putchar(*traverse);
            traverse++; 
        } 

        traverse++; 

        //Module 2: Fetching and executing arguments
        switch(*traverse) 
        { 
            case 'c' : i = va_arg(arg,int);     //Fetch char argument
                        putchar(i);
                        break; 

            case 'd' : i = va_arg(arg,int);         //Fetch Decimal/Integer argument
                        if(i<0) 
                        { 
                            i = -i;
                            putchar('-'); 
                        } 
                        puts(convert(i,10));
                        break; 

            case 'o': i = va_arg(arg,unsigned int); //Fetch Octal representation
                        puts(convert(i,8));
                        break; 

            case 's': s = va_arg(arg,char *);       //Fetch string
                        puts(s); 
                        break; 

            case 'x': i = va_arg(arg,unsigned int); //Fetch Hexadecimal representation
                        puts(convert(i,16));
                        break; 
        }   
    } 

    //Module 3: Closing argument list to necessary clean-up
    va_end(arg); 
} 

char *convert(unsigned int num, int base) 
{ 
    static char Representation[]= "0123456789ABCDEF";
    static char buffer[50]; 
    char *ptr; 

    ptr = &buffer[49]; 
    *ptr = '\0'; 

    do 
    { 
        *--ptr = Representation[num%base]; 
        num /= base; 
    }while(num != 0); 

    return(ptr); 
}
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
vishal
  • 221
  • 2
  • 3
  • Add `if( *traverse == '\0') return;` after `while( *traverse != '%' ){` if you want it to work properly – GabrielT Apr 22 '22 at 09:14
8

If you have some time and are really curious you could study the GNU libc's version: See printf, which in turn uses vprintf, which uses vfprintf

Steffen
  • 2,888
  • 19
  • 19
  • 22
    Telling a newbie C coder to read GNU code? Don't be cruel :-) – finnw Nov 15 '09 at 14:38
  • 1
    @ValentinGolev: well basic code might be easy, but some which uses over hundreds of pointers, lots of data structures, and lots of weird operators to fit long code in one line is going to be really difficult. Especially for a beginner. If you want a feel of it, try reading lots of GNU code. – Box Box Box Box Mar 25 '16 at 03:21
3

Linux va_start(3) man page gives very good example of writing such functions (much more simpler but in general all the major bricks are there). Also you could examine almost any libstdc implementation.

Roman Nikitchenko
  • 12,800
  • 7
  • 74
  • 110
2

There are at least two books with good explanations of how a printf()-like formatting function can be written (and complete working examples):

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • None of these 2 books has an explanation on how it can be written except maybe a bit the first – Miguel M Jan 17 '22 at 00:15
  • That’s odd, @MiguelM . Are you sure you’re looking at the same books that I am? Hanson has chapter 14 on formatting. Plauger has chapter 12 on implementing all the functions needed to support ``. – Jonathan Leffler Jan 17 '22 at 00:21
  • I'll check again. I was pretty sure I didn't see them there but I do admit i did a fast check – Miguel M Jan 18 '22 at 00:44
2

This answer may help you to understand how to write variadic functions. Note, no error / bounds checking is done, no attributes are set to tell the compiler what kind of arguments might be suitable, no benefit over just using printf() is achieved.

It may or may not be the example you are looking for.

The relevant snippet (expanded a bit here):

#include <stdarg.h>
void _printf(FILE *out, va_list ap)
{
    vfprintf(out, fmt, ap);
}

void printf(const char *fmt, ...)
{
    va_list ap;
    va_start(ap, fmt);
    _printf(stdout, ap);
    va_end(ap);
}

Note: making these return the correct type (signed integer) is left as an exercise for the reader. This looks a lot like homework to me, I'm just trying to get you past any sticking point of using va_start and va_end, plus showing that a va_list can be passed to helper functions to avoid duplicating code in so many implementations of almost the same thing.

I strongly advise looking at the BSD (or even glibc) printf sub system implementation. You could also look at uclibc, dietlibc, etc ..

Community
  • 1
  • 1
Tim Post
  • 33,371
  • 15
  • 110
  • 174
0

It only work for '%s' format specifier. But I think this is still useful

void printf(char* str, ...)
{
    char* s;

    va_list vl;
    va_start(vl, str);
    for (char* ptr = str; *ptr != '\0'; ptr++)
    {
        if (*ptr == '%')
        {
            ptr++;
            s = va_arg(vl, char*);
            while (*s != '\0')
                putchar(*s++);
        }
        putchar(*ptr);
    }
    va_end(vl);
}

int main()
{
    char str[60] = "String Array is initialized";
    printf("abcd %s abcd \n", str);
    printf("It work!\n");
}
Jin
  • 1
0
#include <stdio.h>
#include <stdarg.h>
#include <string.h>

// You can write your own low level function to write to display or hardware
void local_put(char c){
    putchar(c);
}

void decimal_to_baseN(char *converted, unsigned int num, int base) 
{
    if (num == 0) {
        converted[0] = '0';
        converted[1] = '\0';
        return;
    }
    int MAX_REP_LEN = 250;
    char NUM_BASE[] = "0123456789ABCDEF";
    char buffer[MAX_REP_LEN];

    int i = MAX_REP_LEN - 1;
    while (num != 0) {
        buffer[i--] = NUM_BASE[num % base];
        num /= base;
    }
    int counter = 0;
    for (int j = i + 1; j <= MAX_REP_LEN - 1; j++) {
        converted[counter++] = buffer[j];
    }
    converted[counter] = '\0';
}


void my_print(char *c, ...)
{
    int num;
    va_list arg;
    va_start(arg, c);
    char outbuf[2048];
    int i;
    char *ch;
    double db_val;
    unsigned int uint_val;
    while(*c!='\0'){
        if(*c=='%'){
            c++; // To incement to the formating character
            // processing the formatting character
            switch(*c){
                case 'b':
                case 'B':
                case 'h':
                case 'H':
                case 'O':
                case 'o':
                case 'd':
                case 'D':
                case 'i':
                case 'I':
                    num = va_arg(arg, int);
                    if(num<0){
                        // Simple - sign is used instead of 2s complement
                        local_put('-');
                        num = num * -1;
                    }
                    if(*c=='b' || *c=='B'){
                        decimal_to_baseN(outbuf, num, 2);
                    }else if(*c=='o' || *c=='O'){
                        decimal_to_baseN(outbuf, num, 8);
                    }else if(*c=='d' || *c=='D'){
                        decimal_to_baseN(outbuf, num, 10);
                    }else if(*c=='h' || *c=='H'){
                        decimal_to_baseN(outbuf, num, 16);
                    }
                    
                    for(int i=0;outbuf[i]!='\0';i++){
                        local_put(outbuf[i]);
                    }
                break;
                
                case 'c':
                case 'C':
                    num = va_arg(arg, int);
                    local_put(num);
                break;
                    
                case 's':
                case 'S':
                    ch = va_arg(arg, char*);
                    while(*ch!='\0'){
                        local_put(*ch++);
                    }
                break;
                
                case 'f':
                case 'F':
                    db_val = va_arg(arg, double);
                    sprintf(outbuf, "%f", db_val);
                    for(int i=0;outbuf[i]!='\0';i++){
                        local_put(outbuf[i]);
                    }
                break;
                
                case 'u':
                case 'U':
                    uint_val = va_arg(arg, unsigned int);
                    sprintf(outbuf, "%u", uint_val);
                    for(int i=0;outbuf[i]!='\0';i++){
                        local_put(outbuf[i]);
                    }
                break;
            }
        }else{
            local_put(*c);   
        }
        c++;
    }
    va_end(arg);
}

int main()
{
    int num = 100;
    my_print("The Decimal: %d\r\n", num);
    my_print("The Unsigned int: %u\r\n", 4294967295);
    my_print("The Binary: %b\r\n", num);
    my_print("The Octel: %o\r\n", num);
    my_print("The Hex: %h\r\n", 999);
    my_print("The Character: %c\r\n", 'C');
    my_print("The String: %s\r\n", "Annie");
    my_print("The Float: %f\r\n", 4.35);
    return 0;
}
manu
  • 21
  • 5
  • Remember that Stack Overflow isn't just intended to solve the immediate problem, but also to help future readers find solutions to similar problems, which requires understanding the underlying code. This is especially important for members of our community who are beginners, and not familiar with the syntax. Given that, **can you [edit] your answer to include an explanation of what you're doing** and why you believe it is the best approach? That's even more critical here when there are existing answers validated by the community. – Jeremy Caney Jul 17 '22 at 01:10
0
    #include <stdarg.h>

int my_printf(const char *format, ...)
{
    va_list args;
    va_start(args, format);

    int i = 0;
    while (format[i])
    {
        if (format[i] == '%')
        {
            i++;
            switch (format[i])
            {
                case 'd':
                {
                    int x = va_arg(args, int);
                    printf("%d", x);
                    break;
                }
                case 'f':
                {
                    double x = va_arg(args, double);
                    printf("%f", x);
                    break;
                }
                case 'c':
                {
                    int x = va_arg(args, int);
                    printf("%c", x);
                    break;
                }
                case 's':
                {
                    char *x = va_arg(args, char*);
                    printf("%s", x);
                    break;
                }
                case 'x':
                case 'X':
                {
                    int x = va_arg(args, int);
                    printf("%x", x);
                    break;
                }
                case 'p':
                {
                    void *x = va_arg(args, void*);
                    printf("%p", x);
                    break;
                }
                case '%':
                    putchar('%');
                    break;
                default:
                    putchar(format[i]);
                    break;
            }
        }
        else
        {
            putchar(format[i]);
        }
        i++;
    }

    va_end(args);
    return 0;
}
int main()
{
    int x = 5;
    double y = 3.14;
    char c = 'a';
    char *str = "Hello World!";
    my_printf("The value of x is %d, y is %f, c is %c, str is %s", x, y, c, str);
    return 0;
}

I think creating a new function without using the printf(), that would do everything of the printf() function is quite tough. So, I tried in this way. Here I've added a documentation for the code- Printf() function

  • The code has undefined behavior if the format string ends with a lone `%`. – chqrlie Jan 29 '23 at 09:20
  • Implementing `my_printf` using `printf` is an interesting approach... removing `printf` is left to the user as an exercise. – chqrlie Jan 29 '23 at 09:22
  • Also the function should return the number of bytes output. – chqrlie Jan 29 '23 at 09:23
  • @chqrlie actually I built it to just see if the simple concept is working or not. I will do something about not only the % but also some other stuffs that printf() function can do but mine does not. Secondly, I'm glad that you liked my approach but I did it because replicating the printf function without using itself is I think quite difficult – Snehasish Konger Jan 29 '23 at 16:43
  • Removing the reference to `printf` is rather easy for most cases in your code, full support for flags, width and precision fields is tedious and tricky but full support for floating point conversions is indeed extremely difficult. Producing the expected output for all values and parameters is one of the most difficult tasks in the C library. – chqrlie Jan 29 '23 at 18:57
  • If you don't mind, I shall modify your code to fix some issues and restrict `printf` use to floating point conversions. – chqrlie Jan 29 '23 at 19:23
  • Sure, why not @chqrlie. If this makes the code error free, surely do it. I'll also learn from it. – Snehasish Konger Jan 31 '23 at 06:09