Vivek Maran
Vivek Maran

Reputation: 2753

Wrong format specifiers in scanf (or) printf

As of the printf function is concerned, I understand the following from few references and experiments.

Similarly, what is the behaviour of scanf, if there is a mismatch of format specifier and the arguements passed to scanf. Does the standards define it?

Upvotes: 4

Views: 4502

Answers (2)

Steve Summit
Steve Summit

Reputation: 48043

printf and scanf are confusing, because they're pretty special and unusual functions. Both of them accept a variable number of arguments. And this means that the rules for matching the types of the arguments, and the automatic conversions that might happen, are different than for other functions.

For most functions, the compiler knows exactly what type(s) of arguments(s) are expected. For example, if you call

#include <math.h>

int i = 144;
printf("%f\n", sqrt(i));

it works fine. The sqrt function expects an argument of type double, but the compiler knows this, so it can insert an automatic conversion, just as if you had written

printf("%f\n", sqrt((double)i));

(Footnote: The compiler knows about sqrt's expected argument type from the function prototype found in <math.h>.)

But printf accepts a variable number of arguments, of any types. You can pass just about anything, as long as the format specifiers match:

int i1 = 12, i2 = 34;
float f1 = 56.78, f2 = 9.10;
printf("%d %d %f %f\n", i1, i2, f1, f2);
printf("%f %f %d %d\n", f1, f2, i1, i2);

But that's the key: as long as the format specifiers match. In a call to a function with a variable number of arguments, like printf, the compiler does not attempt (it's not even allowed to attempt) to convert each argument to the type expected by the corresponding format specifier. So if there are gross mismatches, like trying to use %f to print an integer, it doesn't work, and in fact crazy things can happen.

But, just to keep things interesting, when you call a function that takes a variable number of arguments, like printf, there's another set of conversions that are automatically performed. They're called the default argument promotions. Basically, anything of type char or short is automatically converted ("promoted") to int, and anything of type float automatically converted to double. So you can get away with some mismatches: you can print a char or a short using %d, and you can print either a float or a double using %f. And since char is always promoted to int, that means %c is actually going to receive an int, and this means you can pass a regular int and print it with %c, also.

But it's easy to get this stuff wrong, especially since we're used to the compiler converting everything properly for us when we call other functions, like sqrt. So, a good compiler will peek at the format string, and use it to predict what types of arguments you ought to pass, and if you're passing something wrong, the compiler can issue a warning. If your compiler isn't issuing such warnings, it would be a good idea to figure out how to enable them, or if not, perhaps get a better compiler.

And then we come to scanf. scanf also accepts a variable number of arguments, which are supposed to match the specifiers on the format string. However, for scanf, all of the arguments you pass are pointers, to variables of yours which you're asking scanf to fill in with the values it reads. And it turns out this means that no automatic conversions are possible, and you must pass all arguments as pointers of exactly the right type. Here is a table listing some of them:

specifier corresponding argument
%c pointer to char
%hd pointer to short
%d pointer to int
%ld pointer to long
%f pointer to float
%lf pointer to double
%s pointer to char [note]

So there's no automatic conversion from char and short to int like there is for printf, and there's no automatic conversion from float to double If you're reading a float you have to use %f, and if you're reading a double you have to use %d.

Again, though, a good compiler will warn you about any mismatches, and it's a very good idea to seek out and pay attention to those warnings!

There is one "conversion" that happens automatically when you call scanf, but it has nothing to do with scanf. If you're using %s to read a string, you're supposed to pass an argument of type pointer-to-char, although it has to be a pointer to some number of contiguous characters, because scanf probably isn't going to read a single character; it's probably going to read a string of several characters. So it's pretty common to pass scanf an array of characters, and this is okay, because whenever you use an array in an expression in C, like when you pass it to a function (like scanf), the value that gets used is a pointer to the array's first element, which works just fine, and is what scanf is expecting. That is, both

char *p = malloc(10);
scanf("%9s", p);

and

char a[1-];
scanf("%9s", a);

will work. (And note that this is the one case where you do not need to use a & on a variable you pass to scanf.)

Upvotes: 0

Kerrek SB
Kerrek SB

Reputation: 477512

Variadic arguments (those matching the ellipsis, ...) are default-promoted. That means that all shorter integral types are promoted to int (or unsigned, as appropriate). There's no difference between inte­gers and characters (I believe). The difference between %d and %c in printf is merely how the value is formatted.

scanf is a different kettle of fish. All the arguments you pass are pointers. There's no default-pro­mo­tion among pointers, and it is crucial that you pass the exact format specifier that matches the type of the pointee.

In either case, if your format specifier doesn't match the supplied argument (e.g. passing an int * to a %p in printf), the result is undefined behaviour, which is far worse than being "unpredictable" -- it means your program is simply ill-formed.

Upvotes: 4

Related Questions