1

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.

anuj pradhan
  • 2,777
  • 4
  • 26
  • 31
  • quick link to help http://stackoverflow.com/questions/2457656/understanding-the-hardware-of-printf – Jayesh Bhoi Jul 22 '14 at 06:08
  • @Jayesh thanks for the link but I am not looking for the hardware level. Only the datatype identification in va_arg(). I mean how they actually identify a particular datatype among multiple different datatype arguments – anuj pradhan Jul 22 '14 at 06:10
  • 2
    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. – Brandon Jul 22 '14 at 06:11
  • 1
    `printf` knows the type only from the format specifier. Relevant: [Why do I have to specify data type each time in C?](http://stackoverflow.com/questions/18203636/why-do-i-have-to-specify-data-type-each-time-in-c). – Yu Hao Jul 22 '14 at 06:13
  • Btw, if you are writing in C++ use streams instead of legacy functions from C – mkubacki Jul 22 '14 at 06:17
  • "I mean they are mentioning one data type here" -- because va_arg only fetches one argument. Each va_arg can fetch a different type. printf of course uses the type specified in the % format to select the type. open source printf is available ... google for it. – Jim Balter Jul 22 '14 at 07:00

4 Answers4

2

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.

Sathish
  • 3,740
  • 1
  • 17
  • 28
1

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.

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Ivan Ivanov
  • 2,076
  • 16
  • 33
  • If want to check something refer to http://www.cplusplus.com. They usually have explanation for all libraries. – Ivan Ivanov Jul 22 '14 at 06:13
  • @anujpradhan "i think printf got derived from _printf" -- Not sure what you mean by that, but ... no. printf has existed since the earliest days of C and was not derived from some other function. – Jim Balter Jul 22 '14 at 07:02
0

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");
}
Brandon
  • 22,723
  • 11
  • 93
  • 186
0

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

Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547