anuj pradhan
anuj pradhan

Reputation: 2887

Working of printf() function in C (Only Variable number of Arguments)

I am asking this question to understand the working of printf as a function which is accepting variable length arguments.

I am learning the variable number of argument concept from here and what confused me is the datatype passing in va_arg(va_list,datatype). I mean they are mentioning one data type here. What about the case where we need to pass arguments with different datatypes. The same thing is done in printf function.

How exactly printf figures out the different type of argument types. As per my thinking they must be checking every % symbol in the first const char* argument and after that the token checking for particular datatypes.

Upvotes: 1

Views: 783

Answers (4)

Sathish
Sathish

Reputation: 3870

The Following Variable argument list function explains you how the printf will work.

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

void foo(char *fmt, ...) // This Function works like same as printf
{
    va_list ap;
    int d;
    char c, *s;

    va_start(ap, fmt);
    while (*fmt)
        switch (*fmt++) {
           case 's':              /* string */
               s = va_arg(ap, char *);
               printf("string %s\n", s);
               break;
           case 'd':              /* int */
               d = va_arg(ap, int);
               printf("int %d\n", d);
               break;
           case 'c':              /* char */
               /* need a cast here since va_arg only
                  takes fully promoted types */
               c = (char) va_arg(ap, int);
               printf("char %c\n", c);
               break;
        }
   va_end(ap);
}

main()
{
// call the foo function here
// like foo("%d%s%c",3,"hai",'a');
}

For more reference see the man page of va_arg

It won't support char and float values, so we need to typecast it. For float you need to typecast the double values.

Upvotes: 2

Ivan  Ivanov
Ivan Ivanov

Reputation: 2096

printf is a function, not a macro. It is defined as

int printf(const char *, ...)

and has variable number of arguments.

printf use string to define number of arguments passed. Each % is used to move towards the stack and retrieve arguments.

So, if you passed

"%d %d %d", 1, 2

then 1, 2, and arbitrary value would be displayed. That is bad: you can walk down the stack using this function.

When passed

"%d %d", 1, 2, 3

then 1 and 2 would be displayed. And behaviour is undefined: usually __cdecl calling convention is used, so stack wouldn't be corrupted because cleaned by caller.

Upvotes: 1

Look into the source code of printf in some free software standard C library implementation, e.g. GNU libc or musl-libc. I find musl-libc very readable, look inside src/stdio/printf.c then src/stdio/vfprintf.c (where the real work is done). Of course it uses va_arg according to the format control string (see stdarg(3). Notice that va_arg is implemented inside the compiler, thru __builtin_va_arg in GCC) . GCC also has builtin support for printf

Upvotes: 0

Brandon
Brandon

Reputation: 23500

Yes it checks the %X to see what data type or flags to use. Without this, it cannot know. The only way is if you use C++'s variadic templates. Other than that, C does it like below..

This example uses fwrite to write the data to stdout.

#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <cstdarg>

void C_Printf(const char *fmt, ...)
{
    int fmtLength = strlen(fmt);
    va_list VariableArgs;
    va_start(VariableArgs, fmt);

    for (int I = 0; I < fmtLength; I++)
    {
        if (fmt[I] == '%')
        {
            switch(tolower(fmt[++I]))
            {
                case 'f':
                {
                    double d = va_arg(VariableArgs, double);
                    fwrite(&d, sizeof(double), 1, stdout);
                }
                break;

                case 'i':
                case 'd':
                {
                    int i = va_arg(VariableArgs, int);
                    fwrite(&i, sizeof(int), 1, stdout);
                }
                break;

                case 's':
                {
                    const char *str = va_arg(VariableArgs, const char *);
                    fwrite(&str[0], sizeof(char), strlen(str), stdout);
                }
                break;

                default:
                    break;
            }
        }
        else
            fwrite(&fmt[I], sizeof(char), 1, stdout);
    }
    va_end(VariableArgs);
}

int main()
{
    C_Printf("%s", "hello there");
}

Upvotes: 0

Related Questions