I have verified that the same thing happens to me. I'm going to try to explain what I think should come out, and a possible conjecture why that does not work out.
float x=1, *y, z=3;
x is clear. Take the value 1 initially, but then change the value when you do
x=z+5 , so it ends with the value 8.0. No surprises in that.
y is a pointer type. By doing
y=&x you are assigning the memory address of variable
x . The memory addresses are in the background numbers, 32 bits or 64 bits, depending on the architecture of the CPU and the operation.
Let's say they are 64 bits. Then the variable
x could be stored for example in memory address
00007ffffa19b704 , and when doing
y=&x , you are assigning
y the value
00007ffffa19b704 . These numbers are not invented. They are the ones that have come out when running on Linux.
When you print
y in the second
printf() the compiler should give you a warning since you are specifying
%f as a format string, so expect a variable of type float, but find instead
y , which is type
float* . Still "swallow". When the time comes to execute that call,
printf() will receive the number
00007ffffa19b704 in binary, and will try to decode it according to the IEEE754 standard of floating point of simple precision.
To begin with we have a problem, because according to that standard what you expect is 32 bits, and you are receiving 64. It will possibly interpret only the lower part of that data, that is%
fa19b704 . But it is that decoding this number (for example using this shows that the represented number is
-1.9953335E35 and not 8.00000. First WTF?
z . There are no surprises here either, since
z is a
float that you originally assigned the value
3.0 , and that's exactly what comes out.
Finally you print
&y . Again we are doing something wrong, since
&y is not a
float but the address of a pointer to float, that is, a
float** . But it matters little whether it is a
float* or a
float** . The important thing is that it is (again) a memory address. In this case a different address, since it is the address where the variable
y is stored. Let's say it's
I can say the same as before. When attempting to print that data as if it were float, it may stay with only its low part (
fa19b708 ) and decode it as an IEEE754 float. But it should give
-1.9953343E35 and not
3.0 . Second WTF?
I think that, since what we are giving
printf() in the second and fourth cases are "broken" data (memory addresses instead of floating), if those
addresses result in decoding a floating point error (an invalid number, out of range, infinity, or NaN), possibly . (I no longer think this, see update)
printf() "break" and print anything
My hypothesis is that it returns what it had in a local variable on which the resulting string is built. And that variable is static and therefore contains the result of the previous execution. And that therefore prints the same thing that had printed the
My proof of concept
To verify if my guess is correct, I ran the following program:
float x=1, *y;
What corroborates my hypothesis. Now we only have to clarify why the address of
x is not a valid IEEE754 number: -)
Although the "proof of concept" still points in the direction that
printf() is showing the buffer of a previous conversion, the reasons for this are less clear. Initially I thought that could be due to a bug in the implementation of
printf_fp (the function of the libc that deals with formatting floating point numbers), which behaved badly against certain numbers. But this is not the cause.
The following program copies those same numbers that caused problems (that is, a copy of the pointer value) to another variable, this time of type
double , and
printf() shows it without problems.
double x=8.0, *y;
memcpy(&zz, &y, sizeof(y));
Therefore the cause of the mystery is not in the bytes that
printf() receives in the variable, but in the type of this variable. In a way, what is a pointer instead of a
float makes a difference in what
printf_fp() receives. From there we have undefined behavior (although very consistent).