-2
int var = 8;
int* ptr = &var;
double* ptr2 = (double*) &var;


cout << "The int pointer points to the value: " << * ptr << "\n The Double pointer points to the value: " << *ptr2 << endl;

std::cin.get();

As you can see above, I wrote a super simple program while messing around with pointers (and a little bit of casting), which I'm just learning about. The program compiles and runs fine.

The thing I'm confused about is that the *ptr outputs 8 as expected but the *ptr2 outputs -9.2559592117432085e+61.

When I go to the memory window in the debugger and go to the memory address that's pointed to by both of the pointers, the value stored there is 8, as expected. What's up with the value that ptr2 outputs?

Thanks for reading and any clarifications.

  • 5
    It's [undefined behavior](https://stackoverflow.com/questions/367633/what-are-all-the-common-undefined-behaviours-that-a-c-programmer-should-know-a). Your program had every right to print *any* value, or none at all, or to simply shut down your computer and set fire to itself. You broke the rules of C++, so all bets are off. – Silvio Mayolo Aug 18 '22 at 00:19
  • 2
    This breaks the strict aliasing rule. I suggest you read the following to learn more about it. [What is the strict aliasing rule?](https://stackoverflow.com/questions/98650/what-is-the-strict-aliasing-rule) – WBuck Aug 18 '22 at 00:32
  • 1
    Can you explain exactly why you believe you should get meaningful results from this? What results did you expect to get and why, exactly? – Sam Varshavchik Aug 18 '22 at 00:36
  • 1
    On most modern platforms, a `double` is 8 bytes, whereas an `int` is 4 bytes. Therefore, even if we ignore the strict aliasing violation, the object who's value you are attempting to print will have bytes with indeterminate values. – Andreas Wenzel Aug 18 '22 at 00:38
  • 1
    What you want to look for is "C++ floating point representation", "floating point arithmetic", and maybe some good C++ textbook – Bartosz Charuza Aug 18 '22 at 00:40
  • 1
    The title indicates a misunderstanding. The code isn't casting from int to double; it's casting from pointer-to-int to pointer-to-double. – Pete Becker Aug 18 '22 at 13:02
  • this is type-punning, not casting – phuclv Aug 18 '22 at 13:05

1 Answers1

1

As others commented in the post, it is undefined behavior so the compiler can take any action at this point to print any value, even zero or infinite.

In this specific case, the compiler is actually doing the bare minimum, or perhaps nothing. I reverse compiled your code with a union:

#include <cstdint>
#include <cstdio>
union  DoubleUnion {
    double dval;
    uint32_t uval[2];
};

int main() {
    DoubleUnion uv;
    uv.dval = -9.2559592117432085e+61;
    printf( "%g %08x %08x\n", uv.dval, uv.uval[0], uv.uval[1] );
    return 0;
}

So basically I am placing a double and two 32-bit integers sharing the same space in memory. I then retrofit your printed number inside that double and check what the integers are. The result is this Compiler explorer link

Program stdout
-9.25596e+61 00000008 cccccccc

So basically the first 4 bytes are exactly what you input, an eight. The remaining 4 bytes are 0xcccccccc which is pretty much garbage, what was there after the integer, probably in the stack.

You can obtain the same result with the following (but incorrect) code:

#include <cstdint>
#include <cstdio>
int main() {
    double value = -9.2559592117432085e+61;
    uint32_t* ptr = (uint32_t*)&value;
    printf( "%g %08x %08x\n", value, ptr[0], ptr[1] );
    return 0;
}

Compiler explorer link

UPDATE I though you'd find interesting unpacking the double.

#include <cstdint>
#include <cstdio>
#include <cmath>

int main() {
    struct [[gnu::packed]] Double {
        uint64_t mantissa: 52;
        uint32_t exponent: 11;
        uint32_t sign: 1;
    };
    union DoubleUnion {
        Double fields;
        double value;
        uint64_t uval;
    };
    DoubleUnion du;
    du.value = -9.2559592117432085e+61;
    printf( "Raw values: Double:%g Int:%16lx Sign:%d Exp2:%d Man:%ld\n", 
        du.value, du.uval, du.fields.sign, du.fields.exponent, du.fields.mantissa );

    int64_t mantissa = int64_t(du.fields.mantissa) | (int64_t(1) << 52);
    mantissa = (du.fields.sign) != 0 ? -mantissa : mantissa;
    int32_t exponent = int32_t(du.fields.exponent) - 1075;
    double dval = double( mantissa ) * pow(2,exponent);
    printf("Parsed values: Mantissa:%ld Exponent(2):%d Double:%g\n", mantissa, exponent, dval );

    return 0;
}

Godbolt link

This prints

Program stdout
Raw values: Double:-9.25596e+61 Int:cccccccc00000008 Sign:1 Exp2:1228 Man:3602876265922568
Parsed values: Mantissa:-8106475893293064 Exponent(2):153 Double:-9.25596e+61
Something Something
  • 3,999
  • 1
  • 6
  • 21
  • 1
    in debug mode many compilers will [initialize uninitialized memory to 0xCC to aid debugging](https://stackoverflow.com/q/370195/995714) – phuclv Aug 18 '22 at 01:15
  • @phuclv Interesting I did not know that. Thanks for the info! – Something Something Aug 18 '22 at 01:26
  • Thanks for the useful comments, everyone, especially the detailed response from MadFred. I guess I'm still a little confused as to why this is undefined behavior. The comment from Andreas Wenzel makes the most intuitive sense to me. Is the behavior undefined because an int is 4 bytes, a double is 8 bytes, and therefore having a pointer-to-double pointing to an int means there are 4 bytes "left over" that could have any random value? Thanks in advance for any further clarification. @MadFred – user17654954 Aug 19 '22 at 12:27
  • Undefined behavior also provides a escape for the compiler to implement optimizations in cases where it is not feasible for the language to enforce a certain behavior. So if you take a pointer to a random position in memory, the compiler cannot guarantee that the given memory is being used or not, at least not without a ton of overhead, so the language specs call for undefined behavior. – Something Something Aug 19 '22 at 16:22