0

when i print b and d they both hold the same address ( address of a ). So why does *b print 0 and *d print 5 ?

 void main() 
 {
    double a = 5.0;
    double *d = &a;
    int *b = (int*)d;
    int a1 = 10;
    cout << "Val of D : " << d << " Address of d :" << &d
         << " Value of *d :" << *d << endl;
    cout << "Val of B : " << b << " Address of B :" << &b
         << " Value of *b :" << *b << endl;
}
UpAndAdam
  • 4,515
  • 3
  • 28
  • 46
rahul
  • 47
  • 2
  • 4

3 Answers3

7

Let's make a minor change to the code:

#include <iostream>

int main() {
    double a = 5.0;
    double *d = &a;
    char *b = (char *)&a;
    int a1 = 10;

    for (int i = 0; i < sizeof(double); i++)
        std::cout << std::hex << (int)b[i] << "\t";
}

This shows us the individual bytes of the double as they're stored in memory. The result I get is:

0       0       0       0       0       0       14      40

Now, if you look at the first four bytes of this, they're all zeros. Assuming your int is two or four bytes, when you try to view that memory as an int, the result is going to be zero, because all the non-zero bytes of the double are stored later in memory than the part you're looking at when using it as an int.

Of course, if you printed it out as a long long int instead, you'd get a non-zero result (a long long int is required to be at least 64 bits). Likewise, if you were doing this on a big-endian system, the 14 and 40 bytes of the double would probably be stored at the first bytes in memory rather than the last, so the result would again be non-zero.

The bottom line is that in this case, your cast is roughly equivalent to a reinterpret_cast. Rather than taking the double value and converting it to an int value, it's looking at the bytes of memory occupied by the double, and interpreting them as if they were an int.

Note that the result above isn't really required and you can't count on its happening in portable code. It is quite common and widely expected though (e.g., on most little-endian machines with IEEE floating point and 32-bit ints).

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
  • Additionally: `int i = (int)doubleVal;` is known as a C-style cast and is widely used amongst engineers in what I would term the "C+" phase but is widely considered a bad practice amongst C++ programmers. Is it more to type? Sure, but most IDEs have tools to let you use shortcuts or you can get/buy/write such tools pretty easily and in return you get additional protection/future-proofing from the compiler and the code is more explicit about the author's intention. – kfsone Jun 02 '15 at 00:33
6

Because the double is represented in a totally different format from the int (even if you assume they have the same sizes). A double uses a floating point format, whereas the int does not. Addressing a double with an int pointer is undefined behaviour.

Related: trap representation.

EDIT If you are a beginner, it may seem quite strange that something like int i = 3.14; works, i.e. a double is converted to an int, however a pointer to double CANNOT be used to represent/convert-to an int. That's because in the first case the compiler takes care of the conversion, and automatically truncates 3.14 to 3 then represents the latter as an int, i.e. as 3. Whereas the double representation of 3 looks completely different in the memory.

Community
  • 1
  • 1
vsoftco
  • 55,410
  • 12
  • 139
  • 252
  • Though something like `double d = 1;` or `int i = 3.1415;` is valid? That's so confusing :-P ... – πάντα ῥεῖ Jun 01 '15 at 23:11
  • 1
    @πάνταῥεῖ I actually added a paragraph, since it may not be super obvious for beginners. – vsoftco Jun 01 '15 at 23:21
  • So the reason the above code does not work ( print 5 ) when i type cast a double pointer to an int pointer is because of the format that it is stored in the memory ? – rahul Jun 01 '15 at 23:32
  • @rahul, yes, and because `double` and `int` use different formatting in memory, trying to access a `double` via a pointer to `int` is undefined behaviour, i.e. everything can happen, nothing is guaranteed by the standard. You must avoid undefined behaviour more than the plague. – vsoftco Jun 01 '15 at 23:33
  • @vsoftco: Well, at least if you want your code to be portable you probably want to. There are entirely valid reasons for writing code that makes use of behavior that's not officially defined (e.g., many device drivers do so). Realistically, you also sometimes care more about code effectively porting to specific targets than about what the standard says. Practical portability is a lot more subtle and nuanced than just "don't use UB." – Jerry Coffin Jun 01 '15 at 23:38
  • @JerryCoffin Indeed, but as a beginner I don't think you bump into these issues ;) And probably if you must use such exotic features, you must really know your compiler/architecture. – vsoftco Jun 01 '15 at 23:41
  • C++ does not have trap representations, they're a C thing. (They weren't explicitly mentioned in C89, but C89 was vague and they were introduced in C99 to clear up those vague areas) – M.M Jun 01 '15 at 23:45
  • 1
    @MattMcNabb: If memory serves, C++ is a bit like C89 in this respect--it doesn't mention them directly, but does rule out things that make sense primarily (exclusively?) if they exist. – Jerry Coffin Jun 01 '15 at 23:48
0

This code causes undefined behaviour due to violating the strict aliasing rule.

It may also cause UB due to alignment violation but I'll assume for this post that it doesn't.

The object d has type double. But you try to access its memory through the glvalue *b which has type int. This is not one of the permitted aliasing types that are listed in [basic.lval]/10:

If a program attempts to access the stored value of an object through a glvalue of other than one of the following types the behavior is undefined:

  • the dynamic type of the object,
  • a cv-qualified version of the dynamic type of the object,
  • a type similar (as defined in 4.4) to the dynamic type of the object,
  • a type that is the signed or unsigned type corresponding to the dynamic type of the object,
  • a type that is the signed or unsigned type corresponding to a cv-qualified version of the dynamic type of the object,
  • an aggregate or union type that includes one of the aforementioned types among its elements or non- static data members (including, recursively, an element or non-static data member of a subaggregate or contained union),
  • a type that is a (possibly cv-qualified) base class type of the dynamic type of the object,
  • a char or unsigned char type.
M.M
  • 138,810
  • 21
  • 208
  • 365