How does this program duplicate itself? - c

This code is from Hacker's Delight. It says this is the shortest such program in C and is 64 characters in length, but I don't understand it:
main(a){printf(a,34,a="main(a){printf(a,34,a=%c%s%c,34);}",34);}
I tried to compile it. It compiles with 3 warnings and no error.

This program relies upon the assumptions that
return type of main is int
function's parameter type is int by default and
the argument a="main(a){printf(a,34,a=%c%s%c,34);}" will be evaluated first.
It will invoke undefined behavior. Order of evaluation of arguments of a function is not guaranteed in C.
Albeit, this program works as follows:
The assignment expression a="main(a){printf(a,34,a=%c%s%c,34);}" will assign the string "main(a){printf(a,34,a=%c%s%c,34);}" to a and the value of the assignment expression would be "main(a){printf(a,34,a=%c%s%c,34);}" too as per C standard --C11: 6.5.16
An assignment operator stores a value in the object designated by the left operand. An assignment expression has the value of the left operand after the assignment [...]
Taking in mind the above semantic of assignment operator the program will be expanded as
main(a){
printf("main(a){printf(a,34,a=%c%s%c,34);}",34,a="main(a){printf(a,34,a=%c%s%c,34);}",34);
}
ASCII 34 is ". Specifiers and its corresponding arguments:
%c ---> 34
%s ---> "main(a){printf(a,34,a=%c%s%c,34);}"
%c ---> 34
A better version would be
main(a){a="main(a){a=%c%s%c;printf(a,34,a,34);}";printf(a,34,a,34);}
It is 4 character longer but at least follows K&R C.

It relies on several quirks of the C language and (what I think is) undefined behavior.
First, it defines the main function. It is legal to declare a function without a return type or parameter types, and they will be presumed to be int. This is why the main(a){ part works.
Then, it calls printf with 4 parameters. Since it has no prototype, it is assumed to return int and accept int parameters (unless your compiler implicitly declares it otherwise, like Clang does).
The first parameter is presumed int and is argc at the beginning of the program. The second parameter is 34 (which is ASCII for the double-quote character). The third parameter is an assignment expression that assigns the format string to a and returns it. It relies on a pointer-to-int conversion, which is legal in C. The last parameter is another quote character in numeric form.
At runtime, the %c format specifiers are substituted with quotes, the %s is substituted with the format string, and you get the original source again.
As far as I know, the order of argument evaluation is undefined. This quine works because the assignment a="main(a){printf(a,34,a=%c%s%c,34);}" is evaluated before a is passed as the first parameter to printf, but as far as I know, there is no rule to enforce it. Additionally, this can't work on 64-bit platforms because the pointer-to-int conversion will truncate the pointer to a 32-bit value. As a matter of fact, even though I can see how it works on some platforms, it doesn't work on my computer with my compiler.

This works based on lots of quirks that C allows you to do, and some undefined behavior that happens to work in your favor. In order:
main(a) { ...
Types are assumed to be int if unspecified, so this is equivalent to:
int main(int a) { ...
Even though main is supposed to take either 0 or 2 arguments, and this is undefined behavior, this can be allowed as just ignoring the missing second argument.
Next, the body, which I will space out. Note that a is an int as per main:
printf(a,
34,
a = "main(a){printf(a,34,a=%c%s%c,34);}",
34);
The order of evaluation of arguments is undefined, but we're relying on the 3rd argument - the assignment - getting evaluated first. We're also relying on the undefined behavior of being able to assign a char * to an int. Also, note that 34 is the ASCII value of ". Thus, the intended impact of the program is:
int main(int a, char** ) {
printf("main(a){printf(a,34,a=%c%s%c,34);}",
'"',
"main(a){printf(a,34,a=%c%s%c,34);}",
'"');
return 0; // also left off
}
Which, when evaluated, produces:
main(a){printf(a,34,a="main(a){printf(a,34,a=%c%s%c,34);}",34);}
which was the original program. Tada!

The program is supposed to print its own code. Note the similarity of the string literal to the overall program code. The idea is that the literal will be used as the printf() format string because its value is assigned to variable a (albeit in the argument list) and that it will also be passed as the string to print (because an assignment expression evaluates to the value that was assigned). The 34 is the ASCII code for the double quote character ("); using it avoids a format string containing escaped literal quotation mark characters.
The code relies on unspecified behavior in the form of the order of evaluation of the function arguments. If they are evaluated in argument list order then the program is likely to fail because the value of a would then be used as a pointer to the format string before the correct value was actually assigned to it.
Additionally, the type of a defaults to int, and there is no guarantee that int is wide enough to hold an object pointer without truncating it.
Furthermore, the C standard specifies only two permitted signatures for main(), and the signature used is not among them.
Moreover, the type of printf() inferred by the compiler in the absence of a prototype is incorrect. It is by no means guaranteed that the compiler will generate a calling sequence that works for it.

Related

Is 'printf("%n %d", &a, a)' well defined?

#include <stdio.h>
int main(void) {
int i = 0;
printf("abc %n %d", &i, i);
printf("\n%d\n", i);
}
When I executed that, I got the following result.
abc 0
4
I thought that this result is intended.
But when I execute next one, I got a different result.
int main(void) {
int i; // not initialize
printf("abc %n %d", &i, i);
printf("\n%d\n", i);
}
which produced:
abc 1
4
I don't know why the result of i in the first printf() is 1.
Even, I found more stranger behavior:
int main(void) {
int sdf;
printf("abc %n %d", &sdf, sdf);
printf("\n%d\n", sdf);
int i;
printf("abc %n %d", &i, i);
printf("\n%d\n", i);
}
with this output:
abc 1
4
abc Random_Value
4
The first one always shows 1 but others show random value (I think this is garbage value).
I think garbage value was intended but I didn't get why the first one has a different result.
The value of an uninitialized local integer variable of storage class auto, that is stored in the stack area, is undefined. For this reason, printing it, a random value is absolutely expected.
That's why, in your output
abc 1
4
abc Random_Value
4
the value 1 is garbage as well.
In fact it is the first location in the stack, and its value could be different changing system and/or compiler. My guess is that its value is 1 because it represents a "ghost argc" that, being main a very special function, is present even if the function is defined without parameters, and its value is 1.
Since argc represents the number of parameters used to call your program from command line (at least 1: the executable name) there's a way to verify this hypotesis: call your program in this way
executableName foo
This would make argc become 2, so the value shown by that first printf should become 2 as well.
Out of curiosity, I tested this hypotesis by compiling your second example on my machine (DevC++ with gcc compiler under W10 64bit OS). I confirmed two statements:
When undefined behavior occurs, changing environment leads to a change in the output
argc is present in the stack and affects the initial value of the uninitialized local variables
Output after executing uninitVars.exe
abc
0
abc
1
Output after executing uninitVars.exe dog
abc
0
abc
2
Output after executing uninitVars.exe dog cat
abc
0
abc
3
So it seems that the first four bytes of my stack are always set to 0 (are they the location of the return value?) and that the second one is actually argc, even if it is not explicitly defined in the main() prototype.
The second print, instead, shows different values because it is defined after two printf calls, and their execution write several bytes in the stack (some of them are addresses, and that explains why the value is always different, as the addresses of a process are virtual and always different).
The result is well defined. Address of i and i are passed to the printf. Then printf assigns the value to i. But because i was passed before it, printf will print the value of i before the call.
The latter snippets use uninitialised variable and this undetermined value will be printed first. The behaviour later will be the same as per snipped 1.
The uninitialised variable will have undetermined value. It can be also an UB if the the type of the variable has trap representation (which is not the case here) But because the executing environment is the same, it is very likely that you will see the same values. If you run it on other OS, other computer or will use other compiler they will be probably different.
Is printf(“%n %d”, &a, a) defined well in printf?
If a has type int (equivalently, signed int) and is not const-qualified then yes, otherwise, no. In the well-defined case, the value of a at the time of the call is printed, and after the call returns, a will have the value 0.
Perhaps the key points here are that the arguments to a function call are evaluated before the called function is entered, and they are passed by value. There is a sequence point between the evaluation of the arguments and execution of the first statement in the function body, so the fact that a is read by the calling function and written by printf does not present any particular issue.
[...] when I execute next one, I got a different result.
int main(void)
{
int i; // not initialize
printf("abc %n %d", &i, i);
printf("\n%d\n", i);
}
abc 1
4
I don't know why the result of 'i' in the first printf() is 1.
No one does. The value of an automatic variable that has neither been initialized nor assigned to is indeterminate. The same considerations apply here as in the title question: The expression i in printf's argument list is evaluated before the any part of printf executes, so the fact that printf will later assign a value to it does not affect the value it receives.
With the variable in question not being initialized, the behavior is at best unspecified and at worst undefined.
The local variables i and sdf are uninitialized, which means that their values are indeterminate. The formal definition is in section 3.19 of the C standard and is as follows:
3.19.2
1 indeterminate value
either an unspecified value or a trap representation
3.19.3
1 unspecified value
valid value of the relevant type where this International Standard imposes no requirements on
which value is chosen in any instance
2 NOTE An unspecified value cannot be a trap representation.
3.19.4
1 trap representation
an object representation that need not represent a value of the object type
This basically means that the value is unpredictable. In fact simply reading an indeterminate value can in some cases lead to undefined behavior. This can happen if the indeterminate value happens to be a trap representation as defined as above.
It can also be undefined behavior if the variable in question never had its address taken, however that doesn't apply in this case because you did take the address. This behavior is documented in section 6.3.2.1p2:
Except when it is the operand of the sizeof operator, the
_Alignof operator, the unary & operator, the ++ operator, the
-- operator, or the left operand of the . operator or an
assignment operator, an lvalue that does not have array type
is converted to the value stored in the designated object
(and is no longer an lvalue); this is called lvalue
conversion. If the lvalue has qualified type, the value has
the unqualified version of the type of the lvalue; additionally,
if the lvalue has atomic type, the value has the non-atomic version
of the type of the lvalue; otherwise, the value has the
type of the lvalue. If the lvalue has an incomplete type and does
not have array type, the behavior is undefined. If the lvalue
designates an object of automatic storage duration that could
have been declared with the register storage class (never had its
address taken), and that object is uninitialized (not declared
with an initializer and no assignment to it has been
performed prior to use), the behavior is undefined.
So assuming your implementation doesn't have trap representations, the values of sdf and i are unspecified, which means they could be any value, including 0 or 1. As an example, you get the values 1 and (some random value) for sdf and i. When I run the same code I get this:
abc 0
4
abc 0
4
And if I compile with -O3 which sets a higher optimization level, I get this:
abc 1446280512
4
abc 0
4
As you can see, running the came code as you that reads an unspecified value can have different results on different machines, or even on the same machine with different compiler settings.
There's nothing special about the value 0 that I got or the value 1 that you got. They're just as random as 1446280512.
First of all, you can not print the result of %n in the same line, i will keep its previous value. About the random value, while you have not initialized the i and sdf, they have a random value (most likely 0 but there is no guaranty). In C, global variables will have 0 value if they are not initialized, but the local variables will have uninitialized values (random).

Why is the output of the folowing code 543 instead of 345? [duplicate]

In C and C++, is there a fixed order for evaluation of parameter to the function? I mean, what do the standards say? Is it left-to-right or right-to-left?
I am getting confusing information from the books.
Is it necessary that function call should be implemented using stack only? What do the C and C++ standards say about this?
C and C++ are two completely different languages; don't assume the same rules always apply to both. In the case of parameter evaluation order, however:
C99:
6.5.2.2 Function calls
...
10 The order of evaluation of the function designator, the actual arguments, and
subexpressions within the actual arguments is unspecified, but there is a sequence point
before the actual call.
[Edit]
C11 (draft):
6.5.2.2 Function calls
...
10 There is a sequence point after the evaluations of the function designator and the actual
arguments but before the actual call. Every evaluation in the calling function (including
other function calls) that is not otherwise specifically sequenced before or after the
execution of the body of the called function is indeterminately sequenced with respect to
the execution of the called function.94)
...
94) In other words, function executions do not ‘‘interleave’’ with each other.
C++:
5.2.2 Function call
...
8 The order of evaluation of arguments is unspecified. All side effects of argument expression evaluations take effect
before the function is entered. The order of evaluation of the postfix expression and the argument expression list is
unspecified.
Neither standard mandates the use of the hardware stack for passing function parameters; that's an implementation detail. The C++ standard uses the term "unwinding the stack" to describe calling destructors for automatically created objects on the path from a try block to a throw-expression, but that's it. Most popular architectures do pass parameters via a hardware stack, but it's not universal.
[Edit]
I am getting confusing information from the books.
This is not in the least surprising, since easily 90% of books written about C are simply crap.
While the language standard isn't a great resource for learning either C or C++, it's good to have handy for questions like this. The official™ standards documents cost real money, but there are drafts that are freely available online, and should be good enough for most purposes.
The latest C99 draft (with updates since original publication) is available here. The latest pre-publication C11 draft (which was officially ratified last year) is available here. And a publicly availble draft of the C++ language is available here, although it has an explicit disclaimer that some of the information is incomplete or incorrect.
Keeping it safe: the standard leaves it up to the compiler to determine the order in which arguments are evaluated. So you shouldn't rely on a specific order being kept.
In C/C++ is there a fixed order for evaluation of parameter to the function. I mean what does standards says is it left-to-right or right-to-left . I am getting confusing information from the books.
No, the order of evaluation of function parameters (and of two sub-expressions in any expression) is unspecified behaviour in C and C++. In plain English that means that the left-most parameter could be evaluated first, or it could be the right-most one, and you cannot know which order that applies for a particular compiler.
Example:
static int x = 0;
int* func (int val)
{
x = val;
return &x;
}
void print (int val1, int val2)
{
cout << val1 << " " << val2 << endl;
}
print(*func(1), *func(2));
This code is very bad. It relies of order of evaluation of print's parameters. It will print either "1 1" (right-to-left) or "2 2" (left-to-right) and we cannot know which. The only thing guaranteed by the standard is that both calls to func() are completed before the call to print().
The solution to this is to be aware that the order is unspecified, and write programs that don't rely on the order of evaluation. For example:
int val1 = *func(1);
int val2 = *func(2);
print(val1, val2); // Will always print "1 2" on any compiler.
Is it necessary that function call should be implemented using stack only. what does C/C++ standards says about this.
This is known as "calling convention" and nothing that the standard specifies at all. How parameters (and return values) are passed, is entirely up to the implementation. They could be passed in CPU registers or on the stack, or in some other way. The caller could be the one responsible for pushing/popping parameters on the stack, or the function could be responsible.
The order of evaluation of function parameters is only somewhat associated with the calling convention, since the evaluation occurs before the function is called. But on the other hand, certain compilers can choose to put the right-most parameter in a CPU register and the rest of them on the stack, as one example.
Just only to speak for C language, the order of evaluation inside the function parameters depend on compiler. from The C Programming Language of Brian Kernighan and Dennis Ritchie;
Similarly, the order in which function arguments are evaluated is not
specified, so the statement
printf("%d %d\n", ++n, power(2, n)); /*WRONG */
can produce different results with different compilers,
depending on whether n is incremented before power is called. The
solution, of course, is to write
++n;
printf("%d %d\n", n, power(2, n));
As far as I know, the function printf has an exception here.
For an ordinary function foo, the order of evaluation of foo(bar(x++), baz(++x)) is undefined. Correct! However printf, being an ellipsis function has a somewhat more definite order of evaluation.
The printf in the standard library, in fact, has no information about the number of arguments that has been sent to. It just tries to figure out the number of variables from the string placeholders; namely, from the number of percent operators (%) in the string. The C compiler starts pushing arguments from the very right towards left; and the address of the string is passed as a last argument. Having no exact information about the number of arguments, printf evaluates the last address (the string) and starts replacing the %'s (from left to right) with values of the corresponding addresses in the stack. That is, for a printf like below;
{
int x = 0;
printf("%d %d %f\n", foo(x), bar(x++), baz(++x));
}
The order of evaluation is:
x is incremented by 1,
function baz is called with x = 1; return value is pushed to the stack,
function bar is called with x = 1; return value is pushed to the stack,
x is incremented by 1,
function foo is called with x = 2; return value is pushed to the stack,
the string address is pushed to the stack.
Now, printf has no information about the number of arguments that has been sent to. Moreover, if -Wall is not issued in compilation, the compiler will not even complain about the inconsistent number of arguments. That is; the string may contain 3 %'s but the number of arguments in the printf line can be 1, 2, 3, 4, 5 or even can contain just the string itself without any arguments at all.
Say, the string has 3 placeholders and you've send 5 arguments (printf("%d %f %s\n", k1, k2, k3, k4, k5)). If compilation warning is turned off, the compiler will not complain about excessive number of arguments (or insufficient number of placeholders). Knowing the stack address, printf will;
treat the first integer width in the stack and print it as an integer (without knowing whether it is an integer or not),
treat the next double width in the stack and print it as a double (without knowing whether it is a double or not),
treat the next pointer width in the stack as a string address and will try to print the string at that address, until it finds a string terminator (provided, the address pointed to belong to that process; otherwise raises segmentation error).
and ignore the remaining arguments.

what will happen if we use %p instead of %d while printing an int

Can someone explain the output of the 2nd line?
int x=10;
printf("%d\n",x);
printf("%p\n",x);
printf("%p\n",&x);
Output:
10
0000000a
006aff0c
In your case, it appears to be treating the value 10 as a pointer and outputting it as an eigth-digit, zero-padded-on-the-left, hexadecimal number(a).
However, as per C11 7.21.6.1 The fprintf function, /9 (all standards quotes below also refer to C11):
If a conversion specification is invalid, the behavior is undefined. If any argument is not the correct type for the corresponding conversion specification, the behavior is undefined.
So, literally, anything is permitted to happen. You generally want to avoid undefined behaviour as much as possible, as the behaviour can change between implementations, versions or even the day of the week :-)
There's also technically a problem with your third line. The standard states, in 7.21.6.1 /8 for the p conversion specifier:
The argument shall be a pointer to void. The value of the pointer is
converted to a sequence of printing characters, in an implementation-defined
manner.
Since &x is actually a pointer to int, this puts it in violation of the same contract. You should probably use something like this instead:
printf ("%p\n", (void*)(&x));
(a) What it actually does is implementation-defined, as per the second sentence in the final quote above. It can basically do anything, provided the implementation documents it, as specified in J.3 Implementation-defined behavior:
A conforming implementation is required to document its choice of behavior in each of the areas listed in this subclause.

Result of Printf ("%d", &a)

I have a code in C .
int a;
a =10;
printf ("%d", &a);
I want to know if some garbage will be printed or error msg will be shown.
Basically I am interested in knowing how printf works. Does it have a list of variables in a table sort of things. If a table is there from where it will take the value of &a
This is a beginners question and deserves to be answered properly. (I'm startled by the downvotes and the non-constructive comments)
Let me give a walk-throu line by line:
int a;
The first line declares a variable with the name a. The variable is of type integer and with normal compilers (Microsoft Visual C++ on Windows, GCC on linux, Clang on Mac) this usually 32 bits wide. The integer variable is signed because you did not specify unsigned. This means it can represent values ranging from –2,147,483,648 to 2,147,483,647.
a =10;
The second line assigns the value 10 to that variable
printf ("%d", &a);
The third line is where you get the surprising result. "%d" is the "format string" it defines how the variables, given as further arguments are formatted and subsequently printed. The format string comprises of normal text (which will be printed normally) and control-sequences. the control sequences start with the character % and end with a letter. The letter specifies the argument type that is expected. d in the above case expects a integer value.
The problem with your code is that you do not specify an itenger value, you give the address of an integer value (with the address-of & operator). The correct statement would be:
printf ("%d", a);
Simple.
I recommend that you have a read on a good C book. I recommend "The C programming language" which is from the original authors of the language. You find this book on amazon, but you find it also online.
You can find the same answers reading in the standard. But to be honest, these documents are not easy reading. You can find a draft of the C11 standard here. The description of the formatting options starts on page 309. (drafts are usually good enough for programming purposes, and are usually available for free).
This is undefined behaviour.
If you are new to C, this may be a surprise. However the C specification defines the behaviour of certain programs (called "correct programs"). If you do not follow the rules in the specification, then your program is not correct, and literally anything may happen. The above link goes into more detail.
Your program doesn't follow the rules because the %d format specifier for printf must have a corresponding argument of type int (after the default argument promotions), but instead you pass an argument of type int *.
Since it is undefined behaviour, the output is meaningless and it is generally not worthwhile to investigate.
It will print the address of the variable 'a'. Remember that the & operator returns the address of the operand.

Array of Integers Changes Content When Passed to a Function [duplicate]

In C and C++, is there a fixed order for evaluation of parameter to the function? I mean, what do the standards say? Is it left-to-right or right-to-left?
I am getting confusing information from the books.
Is it necessary that function call should be implemented using stack only? What do the C and C++ standards say about this?
C and C++ are two completely different languages; don't assume the same rules always apply to both. In the case of parameter evaluation order, however:
C99:
6.5.2.2 Function calls
...
10 The order of evaluation of the function designator, the actual arguments, and
subexpressions within the actual arguments is unspecified, but there is a sequence point
before the actual call.
[Edit]
C11 (draft):
6.5.2.2 Function calls
...
10 There is a sequence point after the evaluations of the function designator and the actual
arguments but before the actual call. Every evaluation in the calling function (including
other function calls) that is not otherwise specifically sequenced before or after the
execution of the body of the called function is indeterminately sequenced with respect to
the execution of the called function.94)
...
94) In other words, function executions do not ‘‘interleave’’ with each other.
C++:
5.2.2 Function call
...
8 The order of evaluation of arguments is unspecified. All side effects of argument expression evaluations take effect
before the function is entered. The order of evaluation of the postfix expression and the argument expression list is
unspecified.
Neither standard mandates the use of the hardware stack for passing function parameters; that's an implementation detail. The C++ standard uses the term "unwinding the stack" to describe calling destructors for automatically created objects on the path from a try block to a throw-expression, but that's it. Most popular architectures do pass parameters via a hardware stack, but it's not universal.
[Edit]
I am getting confusing information from the books.
This is not in the least surprising, since easily 90% of books written about C are simply crap.
While the language standard isn't a great resource for learning either C or C++, it's good to have handy for questions like this. The official™ standards documents cost real money, but there are drafts that are freely available online, and should be good enough for most purposes.
The latest C99 draft (with updates since original publication) is available here. The latest pre-publication C11 draft (which was officially ratified last year) is available here. And a publicly availble draft of the C++ language is available here, although it has an explicit disclaimer that some of the information is incomplete or incorrect.
Keeping it safe: the standard leaves it up to the compiler to determine the order in which arguments are evaluated. So you shouldn't rely on a specific order being kept.
In C/C++ is there a fixed order for evaluation of parameter to the function. I mean what does standards says is it left-to-right or right-to-left . I am getting confusing information from the books.
No, the order of evaluation of function parameters (and of two sub-expressions in any expression) is unspecified behaviour in C and C++. In plain English that means that the left-most parameter could be evaluated first, or it could be the right-most one, and you cannot know which order that applies for a particular compiler.
Example:
static int x = 0;
int* func (int val)
{
x = val;
return &x;
}
void print (int val1, int val2)
{
cout << val1 << " " << val2 << endl;
}
print(*func(1), *func(2));
This code is very bad. It relies of order of evaluation of print's parameters. It will print either "1 1" (right-to-left) or "2 2" (left-to-right) and we cannot know which. The only thing guaranteed by the standard is that both calls to func() are completed before the call to print().
The solution to this is to be aware that the order is unspecified, and write programs that don't rely on the order of evaluation. For example:
int val1 = *func(1);
int val2 = *func(2);
print(val1, val2); // Will always print "1 2" on any compiler.
Is it necessary that function call should be implemented using stack only. what does C/C++ standards says about this.
This is known as "calling convention" and nothing that the standard specifies at all. How parameters (and return values) are passed, is entirely up to the implementation. They could be passed in CPU registers or on the stack, or in some other way. The caller could be the one responsible for pushing/popping parameters on the stack, or the function could be responsible.
The order of evaluation of function parameters is only somewhat associated with the calling convention, since the evaluation occurs before the function is called. But on the other hand, certain compilers can choose to put the right-most parameter in a CPU register and the rest of them on the stack, as one example.
Just only to speak for C language, the order of evaluation inside the function parameters depend on compiler. from The C Programming Language of Brian Kernighan and Dennis Ritchie;
Similarly, the order in which function arguments are evaluated is not
specified, so the statement
printf("%d %d\n", ++n, power(2, n)); /*WRONG */
can produce different results with different compilers,
depending on whether n is incremented before power is called. The
solution, of course, is to write
++n;
printf("%d %d\n", n, power(2, n));
As far as I know, the function printf has an exception here.
For an ordinary function foo, the order of evaluation of foo(bar(x++), baz(++x)) is undefined. Correct! However printf, being an ellipsis function has a somewhat more definite order of evaluation.
The printf in the standard library, in fact, has no information about the number of arguments that has been sent to. It just tries to figure out the number of variables from the string placeholders; namely, from the number of percent operators (%) in the string. The C compiler starts pushing arguments from the very right towards left; and the address of the string is passed as a last argument. Having no exact information about the number of arguments, printf evaluates the last address (the string) and starts replacing the %'s (from left to right) with values of the corresponding addresses in the stack. That is, for a printf like below;
{
int x = 0;
printf("%d %d %f\n", foo(x), bar(x++), baz(++x));
}
The order of evaluation is:
x is incremented by 1,
function baz is called with x = 1; return value is pushed to the stack,
function bar is called with x = 1; return value is pushed to the stack,
x is incremented by 1,
function foo is called with x = 2; return value is pushed to the stack,
the string address is pushed to the stack.
Now, printf has no information about the number of arguments that has been sent to. Moreover, if -Wall is not issued in compilation, the compiler will not even complain about the inconsistent number of arguments. That is; the string may contain 3 %'s but the number of arguments in the printf line can be 1, 2, 3, 4, 5 or even can contain just the string itself without any arguments at all.
Say, the string has 3 placeholders and you've send 5 arguments (printf("%d %f %s\n", k1, k2, k3, k4, k5)). If compilation warning is turned off, the compiler will not complain about excessive number of arguments (or insufficient number of placeholders). Knowing the stack address, printf will;
treat the first integer width in the stack and print it as an integer (without knowing whether it is an integer or not),
treat the next double width in the stack and print it as a double (without knowing whether it is a double or not),
treat the next pointer width in the stack as a string address and will try to print the string at that address, until it finds a string terminator (provided, the address pointed to belong to that process; otherwise raises segmentation error).
and ignore the remaining arguments.

Resources