简体   繁体   中英

Why is this code printing 0?

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();

}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM