Using void* and class*

Pages: 12
Is it ok to use a void* to store the address of a class?
You can do that, but why not just use class*?
It is ok even to use a int * to store a unsigned long *.
Every pointer can be a pointer to other types, but be sure to clearly UNDERSTAND what is going on.
...no it isn't.

Pointer types are not interchangeable. The only exception is that it is possible to cast to/from void*.

That said, in most modern architectures pointer types are interchangeable, but this is not by design of the C++ language standard.
@Duoas
Now when you say that it is not by design of the C++ language standard. So what's reinterpret cast for? Please do not misunderstand. I'm asking to know and not argue.
It exists to do things that are not safe and not portable.

MSDN wrote:
The result of a reinterpret_cast cannot safely be used for anything other than being cast back to its original type. Other uses are, at best, nonportable.
The reinterpret_cast operator converts a null pointer value to the null pointer value of the destination type.

The cppreference.com site has a lot of information about type aliasing rules that apply to make the above two points more clear.
http://en.cppreference.com/w/cpp/language/reinterpret_cast

cppreference wrote:
If [the strict type aliasing requirements are not satisified], accessing the object through the new pointer or reference invokes undefined behavior.

This can be just that the referenced data does not have a compatible binary representation, or it can be that the pointers themselves do not have compatible representations. (And yes, machines exist where this is true.)
Last edited on
void* is great for type erasure, which sometimes becomes necessary in C++ when combining OOP and generic programming. std::function, for example, holds a const void* as a member in some implementations. And boost::any holds a void*, of course.

Of course, void* is achieved by static_cast. As for reinterpret_cast, as I mention on that cppreference.com page, it's perfectly legit to cast to char* (e.g. for binary file I/O)
Last edited on
MSDN wrote
The reinterpret_cast operator converts a null pointer value to the null pointer value of the destination type.

didn't get it. Can somebody elaborate it please?
@Pravesh: every pointer has one special "null" value. If it has that value, it is not pointing at any object. If you reinterpret_cast a null pointer of some type to a pointer of another type, the resulting pointer is guaranteed to be the null pointer of that other type.
@Cubbi
Sorry if I'm getting this wrong. But isn't null the absolute pointer 0x0? At least that's what I thought. Can you please enlighten me with an example. It will be very kind of you.
@Pravesh no, the null pointer value is not necessarily 0x0. See the C FAQ: http://c-faq.com/null/machexamp.html
From our point of view as C++ programmers, NULL is zero.

In reality, it may be something else. And the something else may be different depending on what kind of pointer it is. C and C++ hide this from us and let us treat NULL as zero.

Here is some more reading for you: http://c-faq.com/null/machexamp.html and http://c-faq.com/null/index.html

[edit] LOL, this is what I get for being distracted while looking up a response. Same thing with dictionaries. I'm much better now, but when I was a kid I often forgot what word I was actually looking up because I was busy getting distracted by all the other words in there...
Last edited on
C++11 even introduced the nullptr litteral of type nullptr_t to explicitly show the usage of a null pointer. It can be used in function overloading to treat separatly the null pointer case at compile time for example.

For the class* <-> void*:
Is is as legal to put a Toto* into a void* than put Toto* in int* as long as you don't dereference it. The illegal thing is to dereference a pointer of a static type(the one written in the cpp file when the variable is dereferenced) which has a dynamic type(the real type of the variable) not compatible(the types can't alias). The link Duoas gave describe the rules which make a type A alias to another type B.

So for example this piece of code is standard and valid:
1
2
3
4
5
6
7
8
9
10
struct A { int i; };
struct B { int i; };

int main()
{
	A a = {5};
	B* b = reinterpret_cast<B*>(&a);
	reinterpret_cast<A*>(b)->i = 500;
	cout << a.i << endl;
}


and this one is not and will produce stange result depending on the flags passed to the compiler:
1
2
3
4
	A a = {5};
	B* b = reinterpret_cast<B*>(&a);
	b->i = 500;
	cout << a.i << endl;

On gcc with optimisations this output 5 because the standard say *b and a can't be at the same memory location(since A and B are not aliased types) so the line 3 is illegal(and will produce a warning).
Scary isn't it?

But this one is valid:
1
2
3
4
A a = {5};
int* b = reinterpret_cast<int*>(&a);
*b = 500;
cout << a.i << endl;

because int is aliased with A as A contains a member of type int.
Just when I thought that I'm getting hang of this language.
Just when I thought that I'm getting hang of this language.

That's what i though 2 years ago and i still discover new things in C++.
Sometimes I wonder if Bjarne himself get hang of his own language...
It is legal, but the results are undefined. Pointer types are not necessarily compatible, and you may lose some important information by casting around.

That said, if that were not a rarity a lot of code would be seriously broken...
1
2
3
4
	A a = {5};
	B* b = reinterpret_cast<B*>(&a);
	b->i = 500;
	cout << a.i << endl;


On gcc with optimisations this output 5 because the standard say *b and a can't be at the same memory location(since A and B are not aliased types) so the line 3 is illegal(and will produce a warning).
Scary isn't it?


You know.. I looked at that and thought, "why is line 3 illegal?" It's not illegal. b is of type pointer to B and all Bs have a member i as you described it earlier in the post.

Coincidentally, I popped this into Mingw (a gcc variant) and got 500 for -O2 and -O3.
@cire depends on what you call "illegal". It is well-formed (the language grammar is not violated), but the behavior of the program is explicitly described by the standard as "undefined".
So it's as legal as accessing an array out of bounds.
The important thing is type.

Type allows the compiler to check that you're using a given bit pattern in the correct way.

The cast in C/C++ allows you to override the type system and take manual control of the use of specific bit patterns. But then the problem of checking that you're doing the right thing becomes your own.

The rules around override are complicated because they must be done in a platform independent way. For example the 68000 series processor does not allow a pointer to a signed integral value to be used on an unsigned equivalent, so the standard has to accomodate that sort of thing.

Is it ok to use a void* to store the address of a class?
As Duoas said, no it's not. Because you loose the type information, the compiler cannot help you dereference that pointer correctly.

But isn't null the absolute pointer 0x0?
No. Zero is just a value. It's not a type. It's used with pointers to indicate the pointer doesn't point anywhere (that interpretation is what we call type).

1
2
3
4
	A a = {5};
	B* b = reinterpret_cast<B*>(&a);
	b->i = 500;
	cout << a.i << endl;
This code is an example of a programmer bypassing the type system. The compiler can only syntax check that:
1. A can be initialised with 5.
2. A and B have member's called i, but it doesn't check that they're at the same offset within the struct/class.

If A was defined as:
1
2
3
4
struct A
{
  int a, b, c, d, e, f, g, h, i;
};

and B was defined as:
1
2
3
4
struct B
{
  int i;
};

a.i and b->i would not refer to the same bit pattern and the compiler couldn't help as you overrode the type system.

Without type, we may as well be using B or Smalltalk and discover our type errors at runtime.
Last edited on
@cire depends on what you call "illegal". It is well-formed (the language grammar is not violated), but the behavior of the program is explicitly described by the standard as "undefined".
So it's as legal as accessing an array out of bounds.


Well, of course it's undefined behavior. I was surprised mention of a warning was made, and also at the predicted outcome, although I've heard some horror stories about gcc's strict aliasing policy. I would expect, as a QoI issue, that the example given would perform as expected (especially since this is specifically allowed via a union -- standard lay-out struct, common initial sequence.)
Pages: 12