Reputation: 2753
As of the printf
function is concerned, I understand the following from few references and experiments.
%c
to print the character equivalent of the integer value. Also using of %d
to print ASCII value (integer representations) of character is acceptable. 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
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
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 integers 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-promotion 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