1

What is the point of format specifier in C if we have allready set the type of variable before printf?

For example:

#include<stdio.h>
int main(void)
{
    int a=7
    printf("%d", a);
} 

Like, it's allready stated what a is, it's integer(int). So what is the point of adding %d to specify that it's an integer?

17
  • 3
    How would printf know about the type you passed to the function? Commented Jan 24, 2023 at 3:05
  • 4
    Here's an exercise for you that will allow you to answer your own question: Write your own version of printf that doesn't require a format string, but which can handle any number of arguments of type int, double, or char *. You won't be able to do it, for the reasons I just gave. Commented Jan 24, 2023 at 3:06
  • 1
    @jeaq printf doesn't know anything about a, which is a local variable in the caller. All it knows is that the second argument is an int (as indicated by the format string), and after it extracts that argument, it knows it has the value 7. That's all it knows. There's no quantum entanglement taking place. Try to understand that printf is just a function that's being called. The compiler isn't custom-building a different version of it for each call. There's only one. Commented Jan 24, 2023 at 3:33
  • 1
    I think this is actually quite a good question. If you're not steeped in C's ancient history, or if you're used to any number of languages with a built-in print statement, it's quite strange that C's printf function has the strange requirements that it does, and the more so if you haven't realized that printf is in fact an ordinary function. Commented Jan 24, 2023 at 4:55
  • 2
    @Lundin That's your opinion and you're entitled to it, but I'm a professional C and C++ programmer who still uses printf almost every day. Chacun à son goût. Commented Jan 24, 2023 at 12:57

3 Answers 3

3

The answer to this question really only makes sense in the context of C's history.

C is, by now, a pretty old language. Though undoubtedly a "high level language", it is famously low-level as high-level languages go. And its earliest compiler was deliberately and self-consciously small and simple.

In its first incarnation, C did not enforce type safety during function calls. For example, if you called sqrt(144), you got the wrong answer, because sqrt expects an argument of type double, but 144 is an int. It was the programmer's responsibility to call a function with arguments of the correct types: the compiler did not know (did not even attempt to keep track of) the arguments expected by each function, so it did not and could not perform automatic conversions. (A separate program, lint, could check that functions were called with the correct arguments.)

C++ corrected this deficiency, by introducing the function prototype. These were inherited by C in the first ANSI C standard in 1989. However, a function prototype only works for a function that expects a single, fixed argument list, meaning that it can't help for functions that accept a variable number of arguments, the premier example being: printf.

The other thing to remember is that, in C, printf is a more or less ordinary function. ("Ordinary" other than accepting a variable number of arguments, that is.) So the compiler has no direct mechanism to notice the types of the arguments and make that list of types available to printf. printf has no way of knowing, at run time, what types were passed during any given call; it can only rely (it must rely) on the clues provided in the format string. (This is by contrast to languages, many of them, where the print statement is an explicit part of the language parsed by the compiler, meaning that the compiler can do whatever it needs to do in order to treat each argument properly according to its known type.)

So, by the rules of the language (which are constrained by backwards compatibility and the history of the language), the compiler can't do anything special with the arguments in a printf call, other than performing what is called the default argument promotions. So the compiler can't fix things (can't perform the "correct" implicit conversion) if you write something like

int a = 7;
printf("%f", a);

This is, admittedly, an uncomfortable situation. These days, programmers are used to the protections and the implicit promotions provided for by function prototypes. If, these days, you can call

int x = sqrt(144);

and have the right thing happen, why can't you similarly call

printf("%f\n", 144);

Well, you can't, although a good, modern compiler will try to help you out anyway. Although the compiler doesn't have to inspect the format string (because that's printf's job to do, at run time), and the compiler isn't allowed to insert any implicit conversions (other than the default promotions, which don't help here), a compiler can duplicate printf's logic, inspect the format string, and issue strong warnings if the programmer makes a mistake. For example, given

printf("%f\n", 144);

gcc prints "warning: format ‘%f’ expects argument of type ‘double’, but argument 2 has type ‘int", and clang prints "warning: format specifies type 'double' but the argument has type 'int'".

In my opinion, this is a fine compromise, balancing C's legacy behavior with modern expectations.

Sign up to request clarification or add additional context in comments.

1 Comment

"C++ corrected this deficiency, by introducing the function prototype. These were inherited by C in the first ANSI C standard in 1989" --> minor quibble, prior to C89 prototyping was coming into vogue, yet I do not recall it being an inheritance from C++ as much it was a general C improvement. The first C++ "compiler" I used was a translator of C++ into C and then compilation. The C compiler at that time already had prototypes.
1

what is the point of adding %d to specify that it's an integer?

printf() is a function which receives a variable number of arguments of various type after the format argument. It does not directly know the number nor the type of arguments passed nor received.

The callers knows the argument count and types it gives to printf().

To pass the arguments count and type information, the format argument is used by the caller to encodes the argument count and types. printf() uses that format and decodes it to know the argument count and type. It is very important that the format and following arguments passed are consistent.

Comments

1

printf() accepts a variable number of arguments. To process those variable arguments it (va_start()) needs to know the last fixed argument is. It (va_arg()) also needs to know the type of each argument so it figure how much data to read.

The format specifier is also a compact template (or DSL) to express how text and variables should be formatted including field width, alignment, precision, encoding.

7 Comments

Thank you. The reason I posted this question is that it's weird to me that printf knows that a=7 but does not know that a is int. Both of which were stated in previous line. Maybe better question would be, how it knows that a=7 then?
@jeaq — it knows because you specify in the format string that the type of the second argument is int, so it pulls an int out of the variable argument list and formats that, and that value happens to be 7. In advance, it doesn't know that the value is 7; it finds that it is 7 when it looks. Roughly like sqrt(4.0) doesn't know in advance that the value is 4.0; it pops the value off the stack and calculates with it. If you're so minded, you could compare C's printf() family with Pascal's writeln, Fortran (77, 66) and its WRITE statement, and any other language with I/O support.
@jeaq I think this has been well answered by now, but printf does not know that a is an int. The compiler knows, but it has no way to pass that information to printf. As in any function call, only values are passed. And in just about any ABI (this is even more true today than it once was), if a function tries to access a passed argument using a different type than the one actually passed, you get meaningless results.
I got it, thanks everyone. It's basically like this: we specify in printf what type of data we want to print, an integer, and then printf goes to integers and finds variable named a and prints it.
I guess what bothered me was that I was thinking that once we use variable(in this case a) that that is it. no more a variable. But haven't thought about the fact we can have variable(a) in float for example.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.