Jump to content
  • Advertisement
Sign in to follow this  
nomichi

TICPP: ch15 ex 33.

This topic is 4404 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Well I've come across another exercise that I'm not quite sure how to handle.
Quote:
Create a class with data members and virtual functions. Write a function that looks at the memory in an object of your class and prints out the various pieces of it. To do this you will need to experiment and iteratively discover where the VPTR is located in the object.
I'm not sure how to make the function is requires. Any hints would be appreciated.

Share this post


Link to post
Share on other sites
Advertisement
Ask yourself (if you're this far in the book you should have answers):

- How can I get the location in memory where the object instance is located?
- How can I get the size of an object instance?
- Given a location in memory and an amount of bytes (size) to look at, how can I write that data as a sequence of bytes?
- Given the values of the data members, what bytes should I be expecting? What positions are they in? Therefore, what are the other bytes? (Answer: some may be padding, and for a class with virtual functions, one pointer-sized chunk will be the VPTR.)

Share this post


Link to post
Share on other sites
Thanks Zahlman. I hadn't touched this book for about a month and just recently had time to come back to it. I played around for a while and this is what I've come up with so far.


#include <iostream>
using namespace std;

#include <Windows.h>

class MyClass
{
int i;
int j;
double d;

public:
MyClass(const int& ii, const int& jj, const double& dd) : i(ii), j(jj), d(dd) {}
virtual int f() const { return i;}
virtual int g() const { return j;}
virtual double h() const { return d; }

};



int main()
{
MyClass m(10, 15, 10.10);
size_t sz = sizeof(m);
unsigned char* data = new unsigned char[sz];

ZeroMemory(data, sz );
memcpy(data, &m, sz );

cout << "Testing:\n" << endl;
cout << "Calling f(): " << ((MyClass*)data)->f() << endl;
cout << "Calling g(): " << ((MyClass*)data)->g() << endl;
cout << "Calling h(): " << ((MyClass*)data)->h() << endl;

cout << "\nvPtr at [0]?" << endl;
cout << "i at [8]: " << (int)data[8] << endl;
cout << "j at [12]: " << ((int)data[12]) << endl;
cout << "d at [?]: " << (double)data[16] << endl;

cout << "\nSearching:" << endl;
for ( int i = 0; i < sz; i++ )
{
cout << "data[" << i << "] = " << (double)data << endl;
}

delete data;
return 0;
}



I can't seem to find the double in there and I don't really understand why the size of MyClass is 24. I guess it's padding like you mentioned. I'm assuming the vPtr is at data[0] and the double should be at data[16]. So where is the double hiding? [help]

Share this post


Link to post
Share on other sites
When you do:

cout << "i at [8]: " << (int)data[8] << endl;

you are taking a single unsigned char, 8 bytes into data, and casting it to an int. I think you want

cout << "i at [8]: " << *((int*)(data+8)) << endl;

to actually look at the contents of the int there. So I guess

cout << "d at [16]: " << *((double*)(data+16)) << endl;

Let's break this down a bit:

data+8 - pointer to some bytes 8 bytes in from where data points
(int*)(data+8) - above pointer cast to a pointer to int
*((int*)(data+8)) - above cast pointer dereferenced to get the value pointed at

I suppose in C++ this would be more correctly put as:

*(reinterpret_cast<int*>(data+8))

HTH Paul

Share this post


Link to post
Share on other sites
Each individual element of 'data' is just a byte, so none of them on their own represent a 'field' of the structure; thus, reinterpreting each as an int or double for output is not likely to be what you want.

I would suggest:

- Don't copy the structure at all; just set a char* pointer at the beginning of the struct. (You will need to reinterpret_cast for this to work.) But for future tasks where you do need to copy, use proper tools:


// nasty caveman stuff
ZeroMemory(data, sz );
memcpy(data, &m, sz );

// shiny C++ standard library stuff, from <algorithm>
const char* const ptr = reinterpret_cast<char*>(&m);
// Which is all we need, as discussed above, but let's see the replacements:
std::fill(data, data + sz, 0);
// although you don't need to zero out something before you copy over it :P
std::copy(ptr, ptr + sz, data);
// Here's the main benefit: interaction with more complex storage types ;)
// std::vector<char> data;
// std::copy(ptr, ptr + sz, back_inserter(data));


- Don't put in floating-point members, because it's harder to find those in a dump of bytes (the binary representation is quite complex compared to that of ints).

- When you output all the bytes, output as a hex dump.

Share this post


Link to post
Share on other sites
Thanks EC. That worked nicely and I found the double. I guess I only found the ints because they were less than 255. [smile]

I guess I was supposed to do things more how Zahlman was saying so I gave it a shot the "C++ way". [smile] I don't think this book has covered much of reinterpret_cast yet. I think it was mentioned but that's about it. I'm not sure if std::fill or std::copy were covered but I remembered memcpy so I used that and I wasn't sure if I needed ZeroMemory but I did that just to be safe.

Anyway, here is what I have now. Does this look about right? [edit: Oh and thanks Zahlman too!]

#include <iostream>
using namespace std;

#include <Windows.h>
#include <algorithm>
#include <iomanip>

class MyClass
{
int i;
int j;
double d;

public:
MyClass(const int& ii, const int& jj, const double& dd) : i(ii), j(jj), d(dd) {}
virtual int f() const { return i;}
virtual int g() const { return j;}
virtual double h() const { return d; }

};

class NoDouble
{
int i;
int j;

public:
NoDouble( const int& ii, const int& jj ) : i(ii), j(jj) {}
virtual int f() const { return i; }
virtual int g() const { return j; }

};



int main()
{
// Caveman way? =)
MyClass m(10, 15, 10.10);
size_t sz = sizeof(m);
unsigned char* data = new unsigned char[sz];

ZeroMemory(data, sz );
memcpy(data, &m, sz );

cout << "\nFunction Test:\n" << endl;
cout << "Calling f(): " << ((MyClass*)data)->f() << endl;
cout << "Calling g(): " << ((MyClass*)data)->g() << endl;
cout << "Calling h(): " << ((MyClass*)data)->h() << endl;

cout << "\nSearching:\n" << endl;
for ( int i = 0; i < sz; i++ )
{
if ( *(int*)(data + i) == 10 || *(int*)(data + i) == 15 )
{
cout << "Found: " << *(int*)(data + i) << " at (data + " << i << ")" << endl;
}
else if ( *(double*)(data + i) == 10.10 )
{
cout << "Found: " << *(double*)(data + i) << " at (data + " << i << ")" << endl;
}

}

delete data;


// Shiny C++ way?
NoDouble no(333, 1020);
sz = sizeof(no);
const unsigned char* const cp = reinterpret_cast<unsigned char*>(&no);
data = new unsigned char[sz];

std::fill(data, data + sz, 0); // ok i dont need to do this but it's practice using fill :)
std::copy(cp, cp + sz, data); // more practice...

// make the hex output look better
cout.setf(ios::hex | ios::uppercase);

cout << "\nHex Dump:\n" << endl;
for ( int i = 0; i < sz; i++ )
{
// I think Zahlman was saying to use the cp, not a copy?
cout << "cp + " << dec << i << " = " << hex << std::setw(2) << std::setfill('0')
<< static_cast<int>(*(cp + i)) << endl;
}
cout << endl;

delete data;

return 0;
}


Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!