void pointers

Started by
7 comments, last by Antheus 15 years, 11 months ago
Hello, I'm coming from a java background trying to learn c++ from an online book "Thinking in C++." I have come to an interesting exercise in the book and was wondering why it works. Here is the code:

#include <iostream>

using std::cout;
using std::endl;

class Bird
{
public:

	void fly();
};

void Bird::fly()
{
	cout<<"Flying"<<endl;
}

class Rock
{
};

//Flying Rocks

int main(void)
{
	Rock r;
	Bird b;
	void *p;

	p = &r

	b = (*(Bird*)p);
	b.fly();

	return 0;
}
Shouldn't you get some kind of runtime error when running this? Just curious how this works internally.
Advertisement
It's been a while since I've brushed up on my C++, but I've been doing ANSI C all this term, and that is perfectly legal in C.

That said, when you access any fields, you're more likely to get garbage or seg faults than anything else.

C++, unlike Java, assumes you understand what you're doing when you do dumb stuff like this.
Eric Richards
I am not a C++ programmer, but I'm pretty sure it's because fly() references no member variables. If it did, I believe it would segfault. It has nothing to do with (void*)--(void*) just means "a pointer to something". In C++, you can cast anything to anything. Doesn't mean it's legal, but you can do it.

EDIT: I'm playing with it now, and it's a little odd--if you cast into it, it's garbage data, but you can use it like a regular class; I modified the code to have member values and in VC++ 2008 at least it works fine. That said, it is a horrible idea to ever do this.
http://edropple.com
Officially I'm pretty sure this falls in the realm of undefined behavior. This means the C++ specification doesn't address what should happen, therefore your compiler is free to handle this case however it wants.

But anywhoo, here is probably happening on your compiler:

First you cast the Rock to a Bird, there by telling the compiler: "Trust me, this is a bird"

Then you execute the function Bird::fly(). This tells the compiler look for a function named "fly()" that belongs to Birds, then execute it. Since that function doesn't refer to or modify any data members of this I think everything manages to work without producing a runtime error.

The reason why it doesn't matter whether you actually have a Bird or a Rock is because when you create an instance of a class it allocates space for its data members, not its functions. Class functions only occur once in memory and are shared among all instances.
Quote:When you create an instance of a class it allocates space for its data members. No matter how many instances there are of a class, the functions will occur only once in memory. So it doesn't matter that your Bird is really a rock since instances don't actually contain functions.
You can still reference data from a cast instance, it's just junk. C++ stores member variable data as offsets from the pointer, doesn't it? That would make the behavior from my above post make sense.
http://edropple.com
Yeah pretty ugly code and I do not recommended that you code like that!
Stroustroup has good example why you shouldn't do this in his book ,which any serious C++ programmer should own!

AV Rule 182 (MISRA Rule 45)
Type casting from any type to or from pointers shall not be used.
Rationale: This type of casting can lead to undefined or implementation-defined behavior (e.g. certain aspects of memory alignments are implementation-defined). Furthermore, converting a pointer to an integral type can result in the loss of information if the pointer can represent values larger than the integral type to which it is converted.
Exception 1: Casting from void* to T* is permissible. In this case, static_cast should be
used, but "only if it is known that the object really is a T"- my emphasis. Furthermore, such code should only occur in low level memory management routines.


[Edited by - daviangel on May 16, 2008 9:54:55 PM]
[size="2"]Don't talk about writing games, don't write design docs, don't spend your time on web boards. Sit in your house write 20 games when you complete them you will either want to do it the rest of your life or not * Andre Lamothe
Quote:Original post by daviangel
Yeah pretty ugly code and not recommended that you code like that!
As I recall, Eckel warns not to do this, and uses it as an example of bad casting.

http://edropple.com
void pointer are pointers which handles any kind of address..

for example:
Quote:
int *ix;
float *fx;
void *p;

p = (int*)ix;
//or
p = (float*)fx;


Hope this will help you understand..
Just DO it!
Quote:Just curious how this works internally.


Unlike Java, which was designed to be OO from ground up, C++ shares some C legacy.

In C++, member functions (methods) are essentially regular functions which get instance passed to them. Also, unlike JVM which stores type information, in C++ everything is just bytes in memory. You can do '(bird*)22884455', and it may work.

So in above example, you get something like:
void bird_fly(Bird * ptr) // ptr may be anything{  cout<<"Flying"<<endl;}
However, since ptr is never dereferenced in the function, nothing goes wrong (even if NULL, garbage, or invalid instance). It's also a side-effect of release builds, which remove unused variables, thereby never passing ptr.

This is cause of a series of errors when using dynamically allocated standard containers under MVC in release build. In release build, they are generally detected, but release build obscures them, causing completely unrelated failures.

This type of pointer manipulation, while good to know, is something best avoided with passion.

This topic is closed to new replies.

Advertisement