Where do the values of the following variables come from?

4

Could you explain how the variables x, y, z get this value in this code:

    #include <stdio.h>
    #include <stdlib.h>

    main(){

    float x=1, *y, z=3;
    y= &x;
    x=z+5;
    y++;

    printf("%f\n",x); /*8.000000*/
    printf("%f\n",y); /*8.000000*/
    printf("%f\n",z); /*3.000000*/
    printf("%f\n",&y); /*3.000000*/

    }
    
asked by user105251 29.10.2018 в 17:51
source

2 answers

4

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;
y= &x;
x=z+5;
y++;

The variable 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.

The variable 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?

Then print 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 00007ffffa19b708 .

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?

My guess

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 printf() "break" and print anything . (I no longer think this, see update)

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 printf() previous.

My proof of concept

To verify if my guess is correct, I ran the following program:

#include<stdio.h>
#include<math.h>
main(){

    float x=1, *y;
    y= &x;

    printf("%f\n",M_PI);
    printf("%f\n",y);

}

Result:

3.141593
3.141593

What corroborates my hypothesis. Now we only have to clarify why the address of x is not a valid IEEE754 number: -)

Update

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.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

main(){
    double x=8.0, *y;
    double zz;
    y= &x;

    printf("%g\n", 3.14);
    printf("%g\n", y);
    memcpy(&zz, &y, sizeof(y));
    printf("%g\n", zz);
}

Exit:

3.14
3.14
6.95285e-310

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).

    
answered by 29.10.2018 в 18:33
0

the variable * and is a dereference. while & & is reference. I think this could help you understand it better ...
link

    
answered by 29.10.2018 в 18:05