I know that in other languages such as Java when I use System.out.println(); and I put a variable inside of it like an int that holds the number 22 it will print 22 to the console.
In C if I do the same thing with printf(); I need to specify the type in the string such as printf("%d", n); I also know that Java has its own printf function.
What I am trying to get at here is how the C control String works compared to other languages such as Java where you don't have to provide the type identifier in the System.out.println(); and it automatically recognizes the variable is an int.
Is this part of C's way of efficiency and does it not actually check the type and rely's on the programmer to understand the type they are providing?
In fact, printf is neither efficient nor type-safe way of writing data.
There is a performance penalty because format string is parsed at compile time and type specific actions are chosen at run time as well, whereas in C++ and Java it can be done at compile time. Moreover, variadic arguments are forced to be passed on the stack, that is less efficient than passing them in registers.
And what is even more important is that printf is not type-safe. It is possible to pass any number of parameters of any types to printf, ignoring format string prescriptions. Of course, it can easily trigger undefined behavior.
The only reason for such behavior is that there is no function overloading in C.
On the other hand, it's not that bad. First, most of the compilers parse format string and issue a warning if it isn't consistent with parameters passed to printf. Second, aforementioned performance penalty is in fact negligible compared to the cost of formatting and printing text after types have been deduced.
Both C's predefined data types (integers, characters, etc.) as well as user defined types carry no type information with them at run-time. Thus if you use a 4 byte integer in your code, it occupies only 4 bytes in memory (disregarding any padding needed for alignment). This is great for efficiency reasons, but it means that functions that handle multiple data types (like printf) need to be told via the format/control string what the types of the arguments are.
So when printf receives a format string of "%d %f" it knows that the type of the first non-format argument is integer and the second argument is of type float.
By passing control characters in printf() we tell the compiler how to allocate the memory and what is the range of the data that we wanna to print.
Control strings are also known as format specifiers. They are used in formatted input and output operations of the data. Remember, format specifiers are used for formatting the data, for example, in printf() and scanf().
Control strings in scanf() are used to transfer the data to the processor's memory in a formatted way, whereas printf () transfers the data to the output device e.g. monitor screen in a formatted way.
Some common Format Specifierss are listed below:
________________________________________________________________
FORMAT SPECIFIER :: DESCRIPTION
________________________________________________________________
%c Character
%d Signed Integer
%e or %E Scientific notation
%f Floating point
%g or %G. Similar as %e or %E
%hi. Signed Integer
%hu Unsigned Integer(Short)
%i Signed Integer
%l or %ld or %li Signed Integer
%lf Floating point
%Lf Floating point
%lu Unsigned integer
%lli, %lld Signed long long int
%llu. unsigned long long int
%o. Octal representation of Integer.
%p Address of pointer to void void *
void *
%s String char *
%u Unsigned int
Unsigned short int
%n Prints nothing
%% Prints % character
%o Octal representation
%p Address of pointer to void void *
void *
%s String char *
%u Unsigned Integer
%x or %X Hexadecimal representation of
Unsigned Int.
%n Prints nothing
%% Prints % character
________________________________________________________________
Related
This question already has answers here:
Using %f to print an integer variable
(6 answers)
Closed 3 years ago.
I want to know why sizeof doesn't work with different types of format specifiers.
I know that sizeof is usually used with the %zu format specifier, but I want to know for my own knowledge what happens behind and why it prints nan when I use it with %f or a long number when used with %lf
int a = 0;
long long d = 1000000000000;
int s = a + d;
printf("%d\n", sizeof(a + d)); // prints normal size of expression
printf("%lf\n", sizeof(s)); // prints big number
printf("%f", sizeof(d)); // prints nan
sizeof evaluates to a value of type size_t. The proper specifier for size_t in C99 is %zu. You can use %u on systems where size_t and unsigned int are the same type or at least have the same size and representation. On 64-bit systems, size_t values have 64 bits and therefore are larger than 32-bit ints. On 64-bit linux and OS/X, this type is defined as unsigned long and on 64-bit Windows as unsigned long long, hence using %lu or %llu on these systems is fine too.
Passing a size_t for an incompatible conversion specification has undefined behavior:
the program could crash (and it probably will if you use %s)
the program could display the expected value (as it might for %d)
the program could produce weird output such as nan for %f or something else...
The reason for this is integers and floating point values are passed in different ways to printf and they have a different representation. Passing an integer where printf expects a double will let printf retrieve the floating point value from registers or memory locations that have random contents. In your case, the floating point register just happens to contain a nan value, but it might contain a different value elsewhere in the program or at a later time, nothing can be expected, the behavior is undefined.
Some legacy systems do not support %zu, notably C runtimes by Microsoft. On these systems, you can use %u or %lu and use a cast to convert the size_t to an unsigned or an unsigned long:
int a = 0;
long long d = 1000000000000;
int s = a + d;
printf("%u\n", (unsigned)sizeof(a + d)); // should print 8
printf("%lu\n", (unsigned long)sizeof(s)); // should print 4
printf("%llu\n", (unsigned long long)sizeof(d)); // prints 4 or 8 depending on the system
I want to know for my own knowledge what happens behind and why it prints nan when I use it with %f or a long number when used with %lf
Several reasons.
First of all, printf doesn't know the types of the additional arguments you actually pass to it. It's relying on the format string to tell it the number and types of additional arguments to expect. If you pass a size_t as an additional argument, but tell printf to expect a float, then printf will interpret the bit pattern of the additional argument as a float, not a size_t. Integer and floating point types have radically different representations, so you'll get values you don't expect (including NaN).
Secondly, different types have different sizes. If you pass a 16-bit short as an argument, but tell printf to expect a 64-bit double with %f, then printf is going to look at the extra bytes immediately following that argument. It's not guaranteed that size_t and double have the same sizes, so printf may either be ignoring part of the actual value, or using bytes from memory that isn't part of the value.
Finally, it depends on how arguments are being passed. Some architectures use registers to pass arguments (at least for the first few arguments) rather than the stack, and different registers are used for floats vs. integers, so if you pass an integer and tell it to expect a double with %f, printf may look in the wrong place altogether and print something completely random.
printf is not smart. It relies on you to use the correct conversion specifier for the type of the argument you want to pass.
void main()
{
clrscr();
float f = 3.3;
/* In printf() I intentionaly put %d format specifier to see
what type of output I may get */
printf("value of variable a is: %d", f);
getch();
}
In effect, %d tells printf to look in a certain place for an integer argument. But you passed a float argument, which is put in a different place. The C standard does not specify what happens when you do this. In this case, it may be there was a zero in the place printf looked for an integer argument, so it printed “0”. In other circumstances, something different may happen.
Using an invalid format specifier to printf invokes undefined behavior. This is specified in section 7.21.6.1p9 of the C standard:
If a conversion specification is invalid, the behavior is
undefined.282) If any argument is not the correct type for the
corresponding conversion specification, the behavior is undefined.
What this means is that you can't reliably predict what the output of the program will be. For example, the same code on my system prints -1554224520 as the value.
As to what's most likely happening, the %d format specifier is looking for an int as a parameter. Assuming that an int is passed on the stack and that an int is 4 bytes long, the printf function looks at the next 4 bytes on the stack for the value given. Many implementations don't pass floating point values on the stack but in registers instead, so it instead sees whatever garbage values happen to be there. Even if a float is passed on the stack, a float and an int have very different representations, so printing the bytes of a float as an int will most likely not give you the same value.
Let's look at a different example for a moment. Suppose I write
#include <string.h>
char buf[10];
float f = 3.3;
memset(buf, 'x', f);
The third argument to memset is supposed to be an integer (actually a value of type size_t) telling memset how many characters of buf to set to 'x'. But I passed a float value instead. What happens? Well, the compiler knows that the third argument is supposed to be an integer, so it automatically performs the appropriate conversion, and the code ends up setting the first three (three point zero) characters of buf to 'x'.
(Significantly, the way the compiler knew that the third argument of memset was supposed to be an integer was based on the prototype function declaration for memset which is part of the header <string.h>.)
Now, when you called
printf("value of variable f is: %d", f);
you might think the same thing happens. You passed a float, but %d expects an int, so an automatic conversion will happen, right?
Wrong. Let me say that again: Wrong.
The perhaps surprising fact is, printf is different. printf is special. The compiler can't necessarily know what the right types of the arguments passed to printf are supposed to be, because it depends on the details of the %-specifiers buried in the format string. So there are no automatic conversions to just the right type. It's your job to make sure that the types of the arguments you actually pass are exactly right for the format specifiers. If they don't match, the compiler does not automatically perform corresponding conversions. If they don't match, what happens is that you get crazy, wrong results.
(What does the prototype function declaration for printf look like? It literally looks like this: extern int printf(const char *, ...);. Those three dots ... indicate a variable-length argument list or "varargs", they tell the compiler it can't know how many more arguments there are, or what their types are supposed to be. So the compiler performs a few basic conversions -- such as upconverting types char and short int to int, and float to double -- and leaves it at that.)
I said "The compiler can't necessarily know what the right types of the arguments passed to printf are supposed to be", but these days, good compilers go the extra mile and try to figure it out anyway, if they can. They still won't perform automatic conversions (they're not really allowed to, by the rules of the language), but they can at least warn you. For example, I tried your code under two different compilers. Both said something along the lines of warning: format specifies type 'int' but the argument has type 'float'. If your compiler isn't giving you warnings like these, I encourage you to find out if those warnings can be enabled, or consider switching to a better compiler.
Try
printf("... %f",f);
That's how you print float numbers.
Maybe you only want to print x digits of f, eg.:
printf("... %.3f" f);
That will print your float number with 3 digits after the dot.
Please read through this list:
%c - Character
%d or %i - Signed decimal integer
%e - Scientific notation (mantissa/exponent) using e character
%E - Scientific notation (mantissa/exponent) using E character
%f - Decimal floating point
%g - Uses the shorter of %e or %f
%G - Uses the shorter of %E or %f
%o - Signed octal
%s - String of characters
%u - Unsigned decimal integer
%x - Unsigned hexadecimal integer
%X - Unsigned hexadecimal integer (capital letters)
%p - Pointer address
%n - Nothing printed
The code is printing a 0, because you are using the format tag %d, which represents Signed decimal integer (http://devdocs.io).
Could you please try
void main() {
clrscr();
float f=3.3;
/* In printf() I intentionaly put %d format specifier to see what type of output I may get */
printf("value of variable a is: %f",f);
getch();
}
I tried to print character as a float in printf and got output 0. What is the reason for this.
Also:
char c='z';
printf("%f %X",c,c);
is giving some weird output for hexadecimal while output is correct when I do this:
printf("%X",c);
why is it so?
The printf() function is a variadic function, which means that you can pass a variable number of arguments of unspecified types to it. This also means that the compiler doesn't know what type of arguments the function expects, and so it cannot convert the arguments to the correct types. (Modern compilers can warn you if you get the arguments wrong to printf, if you invoke it with enough warning flags.)
For historical reasons, you can not pass an integer argument of smaller rank than int, or a floating type of smaller rank than double to a variadic function. A float will be converted to double and a char will be converted to int (or unsigned int on bizarre implementations) through a process called the default argument promotions.
When printf parses its parameters (arguments are passed to a function, parameters are what the function receives), it retrieves them using whatever method is appropriate for the type specified by the format string. The "%f" specifier expects a double. The "%X" specifier expects an unsigned int.
If you pass an int and printf tries to retrieve a double, you invoke undefined behaviour.
If you pass an int and printf tries to retrieve an unsigned int, you invoke undefined behaviour.
Undefined behaviour may include (but is not limited to) printing strange values, crashing your program or (the most insidious of them all) doing exactly what you expect.
Source: n1570 (The final public draft of the current C standard)
You need to use a cast operator like this:
char c = 'z';
printf("%f %X", (float)c, c);
or
printf("%f %X", (double)c, c);
In Xcode, if I do not do this, I get the warning:
Format specifies specifies 'double' but the argument has type 'char', and the output is 0.000000.
I tried to print character as a float in printf and got output 0. What is the reason for this.
The question is, what value did you expect to see? Why would you expect something other than 0?
The short answer to your question is that the behavior of printf is undefined if the type of the argument doesn't match the conversion specifier. The %f conversion specifier expects its corresponding argument to have type double; if it isn't, all bets are off, and the exact output will vary.
To understand the floating point issue, consider reading: http://en.wikipedia.org/wiki/IEEE_floating_point
As for hexadecimal, let me guess.. the output was something like... 99?
This is because of encodings.. the machine has to represent information in some format, and usually that format entails either giving meanings to certain bits in a number, or having a table of symbols to numbers, or both
Floating points are sometimes represented as a (sign,mantissa,exponent) triplet all packed in a 32 or 64 bit number - characters are sometimes represented in a format named ASCII, which establishes which number corresponds to each character you type
Because printf, like any function that work with varargs, eg: int foobar(const char fmt, ...) {} tries to interpret its parameter to certain type.
If you say "%f", then pass c (as a char), then printf will try to read a float.
You can read more here: var_arg (even if this is C++, it still applies).
I tried to print character as a float in printf and got output 0. What is the reason for this.
Also:
char c='z';
printf("%f %X",c,c);
is giving some weird output for hexadecimal while output is correct when I do this:
printf("%X",c);
why is it so?
The printf() function is a variadic function, which means that you can pass a variable number of arguments of unspecified types to it. This also means that the compiler doesn't know what type of arguments the function expects, and so it cannot convert the arguments to the correct types. (Modern compilers can warn you if you get the arguments wrong to printf, if you invoke it with enough warning flags.)
For historical reasons, you can not pass an integer argument of smaller rank than int, or a floating type of smaller rank than double to a variadic function. A float will be converted to double and a char will be converted to int (or unsigned int on bizarre implementations) through a process called the default argument promotions.
When printf parses its parameters (arguments are passed to a function, parameters are what the function receives), it retrieves them using whatever method is appropriate for the type specified by the format string. The "%f" specifier expects a double. The "%X" specifier expects an unsigned int.
If you pass an int and printf tries to retrieve a double, you invoke undefined behaviour.
If you pass an int and printf tries to retrieve an unsigned int, you invoke undefined behaviour.
Undefined behaviour may include (but is not limited to) printing strange values, crashing your program or (the most insidious of them all) doing exactly what you expect.
Source: n1570 (The final public draft of the current C standard)
You need to use a cast operator like this:
char c = 'z';
printf("%f %X", (float)c, c);
or
printf("%f %X", (double)c, c);
In Xcode, if I do not do this, I get the warning:
Format specifies specifies 'double' but the argument has type 'char', and the output is 0.000000.
I tried to print character as a float in printf and got output 0. What is the reason for this.
The question is, what value did you expect to see? Why would you expect something other than 0?
The short answer to your question is that the behavior of printf is undefined if the type of the argument doesn't match the conversion specifier. The %f conversion specifier expects its corresponding argument to have type double; if it isn't, all bets are off, and the exact output will vary.
To understand the floating point issue, consider reading: http://en.wikipedia.org/wiki/IEEE_floating_point
As for hexadecimal, let me guess.. the output was something like... 99?
This is because of encodings.. the machine has to represent information in some format, and usually that format entails either giving meanings to certain bits in a number, or having a table of symbols to numbers, or both
Floating points are sometimes represented as a (sign,mantissa,exponent) triplet all packed in a 32 or 64 bit number - characters are sometimes represented in a format named ASCII, which establishes which number corresponds to each character you type
Because printf, like any function that work with varargs, eg: int foobar(const char fmt, ...) {} tries to interpret its parameter to certain type.
If you say "%f", then pass c (as a char), then printf will try to read a float.
You can read more here: var_arg (even if this is C++, it still applies).
As of the printf function is concerned, I understand the following from few references and experiments.
When we try to print an integer value with format specifiers that are used for float (or) double and vice the versa the behaviour is unpredictable.
But it is possible to use %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?
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.
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.)