'How does printf("%d",x) work/interpreted?

I am wondering how exactly printf("%d",x) is interpreted. All I know that the compiler reserve a memory to put '%','%d','\0' and returns its address to printf, and then it prints the second argument x based on the format specifier that we indicated.

So if I use for example int x=5; char *p="%d"; printf(p,x), is it the same thing as printf("%d",x)?



Solution 1:[1]

How is printf("%d",x) is interpreted?

OP's explanation is one potential partial description of code.

Yet C and C++ are typically compiled, not interpreted. Compilers today can examine printf("%d",x) and emit code like print_an_integer(x);.


With OP's example, the 2 codes are functionally the same, yet a compiler may not recognize optimization potential as described above with the first code.

int x=5;
// In C++, should be `const char *p="%d";`
char *p="%d";
printf(p,x);
// same as ????
printf("%d",x);

Instead the format string passed to printf() is processed, looking for characters to print and print specifiers. The print specifiers, in turn, get the next argument and process them accordingly.

If the format string and the arguments do not match, like printf("%f",x) or p="%d"; printf(p, 5.0), the result is undefined behavior.


... how exactly ...

A compiler is allowed wide latitude on forming the emitted code. It needs to meet the equivalent functional requirements of a virtual machine - not OP's explanation. Any exact explanation is compiler and code dependent.

Solution 2:[2]

One mildly curious aspect of C is that the printf function, although it's an unusual function, is still very much a function, so all the usual rules apply. And for any function f(), if you have a call with a complicated argument, like

f(complicatedargument1, arg2, arg3);

you can always change it to

sometype tmp = complicatedargument1;
f(tmp, arg2, arg3);

If you haven't learned it yet, you should definitely learn it at some point: expressions in C (and most programming languages) are very, very general. So you can not only have

a + b

you can have

(arbitrarily-complicated expression with many subexpressions) + (arbitrarily-complicated expression with many subexpressions)

You can not only have

f(a, b, c)

you can also have

f((complicated subexpression), (another complicated subexpression), (yet another complicated subexpression))

(And if you want to get really crazy, it turns out that you can replace f by a complicated subexpression, too.)

And because expressions are general like this, the answer to your second question just has to be "yes".

So in principle there is nothing wrong with writing

char *p = "%d";
int x = 5;
printf(p, x);

and this will indeed behave exactly as if you had written

printf("%d", x);

To be very clear: this works because printf's first argument is a string. The vast majority of the time it's a constant string, but there's no reason it can't be some other expression of type "string", such as the variable p. (Footnote: C doesn't really have a "string" type, but that's a separate topic which I'd rather not get into just now.)

In practice, though, there are one or two differences, related to the fact that printf is peculiar.

One issue is that since there are so many easy mistakes you can make when calling printf, good compilers try to help you out, by checking for some of these mistakes. If you accidentally write

int x = 5;
printf("%f", x);

that's not going to work, and gcc helpfully says "warning: format '%f' expects argument of type 'double', but argument 2 has type 'int'". This is something very special that the compiler does, but it's typically able to do it only if the first argument to printf is a string literal. If you were to write

int x = 5;
char *p = "%f"
printf(p, x);

the compiler probably wouldn't be able to perform this check for you.

The other issue is that printf can be dangerous. Very occasionally, you might be doing something exotic where you wanted to use different format strings under different circumstances, like this:

int x = 123;
char *p;
if(somecondition)
     p = "%x\n";
else p = "%d\n";
printf(p, x);

This either prints x in decimal or hexadecimal, depending on somecondition. That's perfectly fine, but what if you got really crazy and wrote:

int x = 123;
char fmt[20];
printf("What format would you like to use? ");
fgets(fmt, 20, stdin);
printf(fmt, x);

Now the user can actually type in the format string that printf is going to use! And this will work, too, and you might want to type it in now and play with it.

...And if you're feeling adventurous, and you do type it in, and after playing with it with formats like %d and %x and %010o, see what happens if you type in the format string %n. On my computer, at least, this crashes! What's up with that?

Well, %n is a very special format specifier for printf, that lets you find out how many characters it's printed so far. But the corresponding argument for %n has to be a pointer to an int. So in this case it will do something like wrongly interpreting the value of x as a pointer, meaning that it tries to write to location 123 or something, which is illegal, and results in a crash.

So, the bottom line here is that using something other than a constant string literal as the first argument to printf is risky, because the compiler won't be able to check for mistakes. And if the format string ends up being derived from something uncontrolled, like user input, this can be downright dangerous.

So as a general rule, it's a good idea not to make much use of this freedom. That is, it's a good idea to keep most or all of your printf format strings as constant string literals. Be careful if you ever use an arbitrary expression there instead, because the compiler can't check it. And unless you're absolutely sure you can trust your users, don't ever let them specify the printf format your program will use, because that will let them crash or possibly even subvert your program.

For this reason, some style guides mandate that the first argument to printf be a string literal. See also this question.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 costaparas
Solution 2