Thread: Casting confusion

  1. #1
    Registered User
    Join Date
    Mar 2009
    Posts
    43

    Casting confusion

    Hi everybody :-)

    I'm hoping someone can explicitly explain to me how casting works. I understand the theory: "conversion of an object of one type to be of another type, as long as the conversion makes sense." However, as a practicality I'm not sure how this works. For example, take the following code extract:

    Animal Bear ("Yogi", 5); // creates a new object of type animal
    ofstream fout (YogiFile, ios::binary); // create new ofstream object for editing Yogifile in Binary code
    fout.write ((char*) &Bear, sizeof Bear); // write Bear object to YogiFile

    The book I'm learning from doesn't explain this so well - it states that the read() and write() functions are expecting a char pointer as the first parameter and so we must cast the address of the object to be a char pointer. So far so good. However it doesn't go into the details of how this is possible - I don't understand how you can convert the address of a user-created object to be a char pointer - I would've thought the two would surely be incompatable.

    Whilst I understand the basic of how to implement casting, I really need to know how it works before I can move on. Any help would be much appreciated!! Cheers :-)
    Last edited by Know_Your_Role; 05-21-2009 at 12:44 AM.

  2. #2
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by Know_Your_Role
    So far so good. However it doesn't go into the details of how this is possible - I don't understand how you can convert the address of a user-created object to be a char pointer - I would've thought the two would surely be incompatable.
    The object is stored in memory. So, if you have a pointer to the starting point of where the object is stored in memory (obtained by &Bear), and we know how many bytes the object occupies (sizeof Bear), we can then traverse over the representation of the object in memory.

    The catch is that if Bear is not of a POD-type (as in "plain old data"), then its layout in memory is implementation defined, so you generally should not write it to file in this way.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  3. #3
    Registered User
    Join Date
    Mar 2009
    Posts
    43
    Call me thick but I haven't followed... lol

    "... we can then traverse over the representation of the object in memory."

    I'm not sure what you mean by this. Are you meaning the data itself remains the same but the way it's represented (ie type) is changed as we overlook its current type in memory?

    I must admit I've never quite been sure how char* works, which probably isn't helping me to understand this conversion!! Does it mean it's literally pointing to the characters themselves? In which case I guess it would make more sense (since char* would start at the beginning of &Bear, and the data could be written to YogiFile as a result of this change).

    lol the ramblings of a true noob... please correct me wherever I'm going wrong, which is probably statements above!!

  4. #4
    The larch
    Join Date
    May 2006
    Posts
    3,573
    Suppose the bear class looked like this:

    Code:
    class Bear()
    {
         int age;
         int height;
         char name[12];
         //...
    };
    Suppose the age is 0x0000000D, height is 0x000000C8 and name is "Teddy". A bear object in memory is a bunch of bytes whose values might look like this: 0, 0, 0, 0x0D, 0, 0, 0, 0xC8, 'T', 'e', 'd', 'd', 'y', '\0', + 6 more bytes of garbage at the end of the name string. When you reinterpret-cast the bear object to a char pointer, this is how it will appear.

    The catch is, that if the object was more complicated and for example used a std::string for the name member, this cast would do no good: a std::string would just contain pointer(s) but storing the pointer addresses in a file won't let you reconstruct the string that the pointers pointed to.
    I might be wrong.

    Thank you, anon. You sure know how to recognize different types of trees from quite a long way away.
    Quoted more than 1000 times (I hope).

  5. #5
    Registered User
    Join Date
    Mar 2009
    Posts
    43
    Right, I think I'm getting somewhere with this now lol. You're essentially saying that the data is the same (0, 0, 0, 0x0D, 0, 0, 0, 0xC8 etc) but now a char pointer is pointing to this data instead of being at an object address?! (this is in order for fout.write() to be able to write this data to the YogiFile).

    Am I getting warm?!! Thank you for your perseverance!!

  6. #6
    Registered User
    Join Date
    May 2007
    Posts
    147
    Casting is a dark and winding subject with some history of this language involved. In modern C++ it is an undesirable evil which we seek to avoid, but use when required with some care. There are several tributaries in the subject.

    The cast syntax you show in your example is a "C" style cast, as in (char *)b. This cast syntax has been replaced in C++ by "static_cast<char *>( b )", which does the same thing with better compiler 'knowledge' to help you avoid making invalid casts.

    Some casts are used to imply a conversion between simple numeric types. For example, if I attempt to assign an int to a double as in:

    double b = 1.0;
    int a = b;

    The compiler may warn that there will be a loss of precision. It will perform a conversion as best can be done, given the fact that an int can't handle all of the possible values a double could hold, but if you know the double is "in range" and you want truncation of the fractional part, you can inform the compiler to stop warning you by casting:

    int a = (int) b;

    This is a "C" style cast of the double to an int, and informs the compiler you really mean this, it's not a mistake (though it's up to you, now, to make certain of that).

    Casting commonly applies to pointers, as you've illustrated. Unlike the previous cast, most pointer casting (of the static_cast or the "C" style cast variety) do not involve a transformation of the underlying data. Nothing is done to "convert" the object being pointed to (unlike the cast above, which implies a conversion of the double).

    Under the hood, pointers are simply a location in memory. If you consider "C" (and therefore C++) as a kind of assembler language, the pointer is nothing more than such a memory location - it has no type from the viewpoint of the CPU.

    The compiler does, however, consider the "type" of the data the pointer identifies, and the programmer has given that type as in:

    char * x;
    player * p;

    In these two cases, both p and x are pointers - just locations in memory. The compiler will recognize that p is of type player. In C, programmers (all to frequently) use void * to refer to a location in memory without any regard to type, as in:

    void * z;

    In this case, z is a pointer like any other, but it's type is void - or one could say unspecified, just the way the CPU 'sees' it. If I pointed z to the location of x, such that the numeric value of x and z were the same, the compiler wouldn't let me write something that used the content were z is pointing as if it were a character. It would require that I inform it that the content should be used as a character. For that, I must cast.

    * ((char *) z) = 'a';

    I tend to overuse parenthesis for clarity here. When z is cast as a char *, the compiler now knows I intend to use it as if it were a char * instead of a void *. The C++ casting operator works, too:

    *( static_cast<char *>( z ) ) = 'a';

    The compiler is smarter with stat_cast than it is with the "C" style cast. The older "C" style cast equates to C++ reinterpret_cast operator, where any conversion that doesn't even make sense can be performed. As such, C++ will complain if a static_cast is attempted that isn't reasonably possible with some degree of certainty (like, the types involved in the cast are somehow related). reinterpret_cast is considered "dangerous" because of this, and so, too, are "C" style casts.

    Casts can apply in other contexts, too. I've pointed out the numeric conversion, and a pointer cast, but you can cast the return from a function, an object, a reference - there are restrictions in some cases, and their uses can wait in your study.

    You may need to understand dynamic_cast, however, at this point. It's use is limited, but is likely the second type of cast you should try if the compiler complains about the use of a static_cast, and the pointer is an object (class or struct). The dynamic_cast is required if the object points to a class or struct with virtual functions (has a vtable), and is more complicated than a simple cast can support. Under the hood, it gets a little messy, but the point is that there may be a runtime adjustment of the pointer required which can't be handled by a "C" style or static_cast, but is handled by dynamic_cast. I'll leave it to you to research this further, just be aware that on objects with virtual functions or complicated inheritance where static_cast generates an error, reach for the dynamic_cast before you try a "C" style or reinterpret_cast (these last two may stop the compiler complaint, but may create a crash at runtime instead).

  7. #7
    Registered User
    Join Date
    Mar 2009
    Posts
    43
    JVene the above is a big help, thank you. I'm at work now lol, but this evening I'll take another look at it & if I have any further questions I will post.

    Cheers! :-)

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Advantages of c++ type casting over c type casting
    By kaibalya2008 in forum C++ Programming
    Replies: 10
    Last Post: 05-05-2009, 11:09 AM
  2. Casting Question (I think)
    By fayte in forum C Programming
    Replies: 6
    Last Post: 03-08-2006, 05:31 PM
  3. casting the system exstracted date into seperate ints
    By bazzano in forum C Programming
    Replies: 1
    Last Post: 08-30-2005, 12:17 AM
  4. Type casting
    By Lionmane in forum C Programming
    Replies: 28
    Last Post: 08-20-2005, 02:16 PM
  5. question about casting pointers/other types also??
    By newbie02 in forum C++ Programming
    Replies: 3
    Last Post: 08-07-2003, 05:01 AM