Reputation: 972
In C, we can convert void*
to any other pointers.
But C++ forbids it.
int *a = malloc(4);
leads to this error:
invalid conversion from ‘void*’ to ‘int*’ [-fpermissive]
Are there any latent dangers here in c++?
Are there any examples of c++?
Upvotes: 13
Views: 3830
Reputation: 331
Casting between void* and the fundamental types (int*, float* etc.) are more obvious.
But, because of inheritance, more unnoticeable problems will emerge.
#include <stdio.h>
struct A
{
int x;
};
struct C
{
int y;
};
struct B : C, A
{
int z;
B()
{
x = 1;
y = 2;
z = 3;
}
};
int main()
{
B* b = new B();
A* a = (A*)(void*)b;
printf("%d\n", a->x);
return 0;
}
the code tries to cast from B to A, it seems OK but actually not. the output is 2.
Note that multiple inheritance is not recommended in practice, but it's still legit.
if void* is used between function parameters and allow implicit casting, things will be out of control, or at least will be a mess.
void* erase the type in a unsafe way. C++ provides safe way of type erasure, i.e. std::any: https://en.cppreference.com/w/cpp/utility/any
Upvotes: 0
Reputation: 6831
The reason you cannot implicitly convert from void *
is because doing so is type unsafe and potentially dangerous. C++ tries a little harder than C in this respect to protect you, thus the difference in behavior between the two languages.
Consider the following example:
short s = 10; // occupies 2 bytes in memory
void *p = &s;
long *l = p; // occupies 8 bytes in memory
printf("%ld\n", *l);
A C compiler accepts the above code (and prints garbage) while a C++ compiler will reject it.
By casting "through" void *
, we lose the type information of the original data, allowing us to treat what in reality is a short
as a long
.
Upvotes: 12
Reputation: 62616
In C++, if you use malloc
, you should construct your object in the storage that malloc
returned. If you just cast the result, your program has undefined behaviour.
int * a = new (malloc(sizeof(int))) int;
But mostly, you shouldn't call malloc in C++. You shouldn't have raw pointers that carry ownership of the pointed-to object.
auto a = std::make_unique<int>();
Upvotes: 1
Reputation: 58530
You can do the conversion in C++, but it requires a cast. C++ aims to be a more type-safe language than C, so it tries to close some "holes in the type system" that C allows.
In C, this will be accepted without any diagnostics being required:
int x;
void *p = &x;
double *q = p;
*q = 0.0;
Type safety has been violated without any casts being present in the source code.
This is a C++ FAQ answered by B. Stroustrup, inventor of C++.
C++ doesn't ban the conversion; it just wants it to be documented by some kind of blurb in the source code, namely a cast which at least suggests, if not proves, that it's being done on purpose.
About the origin of void *
, Stroustrup writes this (bolding mine):
Late in its history, C with Classes* began to support the notion of a pointer to ‘‘raw memory,
void *
. The origin ofvoid *
is shrouded in some mystery. I vaguely remember inventing it together with Larry Rosler and Steve Johnson. However, Dave Prosser remembers first having suggested it based on something used "somewhere in Australia." Possibly both versions are correct because Dave worked closely with Larry at the time. In either case,void *
was introduced into both languages more or less at the same time. The earliest mention ofvoid *
that I can find is in a memo dated January 1, 1983, about the memory management mechanisms provided by my C++ compiler, Cfront, so the origins ofvoid *
in C++ must go back at least to mid-1982. The earliest written record ofvoid *
in the context of ANSI C is a proposal by Mike Meissner dated "12 Oct 83," which presentedvoid *
essentially as it was accepted into ANSI C in June 1984 [Prosser,2001]
So C++ inherits the void *
concept as Stroustrup originally envisioned it, while around the same time, the C people had a slightly different idea that was more loose in type safety.
In the case of malloc
, of course there are dangers; but those are already flagged by the presence of the well-known identifier malloc
; the cast doesn't bring in anything more, but not requiring it specifically for malloc
, while requiring it everywhere else would be an awkward exception to the rules.
* "C with Classes" is the predecessor to C++
Upvotes: 5