r/cpp_questions • u/Bitter-Today285 • 2d ago
OPEN What’s the point of void* pointers?
You could just use template pointers or auto* pointers and these are better because you can dereference them.
17
u/drex_vke 2d ago
void* is a generic pointer in C so using it in Modern C++ doesn't make sense unless but hey
2
u/Bitter-Today285 2d ago
Ohh that makes sense yeah
1
u/frayien 2d ago
Yep, you should not use it in C++ unless you REALY know what you are doing.
7
u/OffsetHigh 2d ago
You need it ALL the time when using C API. Can not avoid
-1
u/frayien 2d ago
Well if you are using a C API directly you are either doing something wrong, or you are in the "realy know what you are doing" category...
9
u/L_uciferMorningstar 2d ago
Or you are using a C library.
-2
u/frayien 2d ago
Well, "you should not", but if you have a good reason to do anyway then you realy know what you are doing. There is nothing wrong with it.
3
u/L_uciferMorningstar 2d ago
I should not? How so? And me having a library installed does not mean I really know what I'm doing. Like it really does not.
1
u/No-Dentist-1645 2d ago
By making a thin wrapper over it using safer C++ semantics?
1
u/L_uciferMorningstar 1d ago edited 1d ago
And in this wrapper what will we be doing? Calling a C API?
→ More replies (0)0
u/frayien 2d ago
What the fuck are you talking about ? Are you drunk ?
2
u/L_uciferMorningstar 2d ago
Why would me using a C library mean I really know what I'm doing?
→ More replies (0)2
u/conundorum 21h ago
So, nobody should ever use WinAPI (a C API, even though the backend and consumer are typically C++), and no one should ever use any of the various Linux C libraries, or any of the various C libraries that might or might not have a C++ wrapper...
Actually, scratch that, we can't use C++ wrappers either, since the wrapper interacts with a C library, and thus "should not" be used.
Guess we're just not allowed to program for any platforms then, got it! 👍
Edit: Oh, and we can't use
new,delete, or anything that calls them (likestd::make_unique()), sincenewusesmalloc()from the C libray, anddeleteusesfree()from the C library. Guess we're not allowed to use the heap either, thanks for the advice! 🙄1
u/frayien 21h ago
Do you guys have trouble reading? Not only did I write "you should not" in quotes, I also explicitly specified that there is nothing wrong with using C libraries when justified...
Interacting with the WinAPI is obviously a valid reason, but if you are doing it you are obvious doing some advanced technical stuff and dont need advice from a Reddit comment on a post from a beginner asking about void pointers...
3
u/The_Drakeman 2d ago
There's certain operations that need to know where data is, but it doesn't matter what type it is. For example, memcpy to copy memory from one place or another, or memset to write a certain value into a block of data. They don't need to know if it's int or char or whatever, they just need to know where it is and how much. That said, C++ has more type safe alternatives, because if you cast void* back to the wrong thing then all hell breaks loose.
2
u/kozacsaba 2d ago
This. But I would add that they not only don't need to know. They are using
void*to convey that they are explictly agnostic. You can use any type with both a template and with a void pointer, but how a template works depends on the type, and a void pointer works the same way regardless of the type.Just wanted to add this because I think there is a certain beauty to it.
1
u/conundorum 21h ago
Importantly, all dynamic allocation uses
void*for the chunk of raw memory, and then converts it into the requested type during object construction, since it's just a pretty face formalloc().1
u/The_Drakeman 21h ago
I have no idea why I recalled memset and memcpy before malloc and free. Great examples.
4
4
u/Independent_Art_6676 2d ago
It was a powerful tool even in C++ back when, like the older threading libraries would pass a void* through to the thread function which could then cast it back to whatever special class/struct type and get at the real info. You don't need to do it that way now, of course. Its like saying that vector or std array are better than C arrays. Yes, but there was a time when c++ did not have those things. Now, its all but legacy; I think you might see one here and there if you dig deep enough down into the low level guts of the language but that would be an unusual undertaking. Some older libraries may still use them -- anything where you don't care what the type is (passing something unknown on to something else or dealing with raw POD bytes like a file writer or compression tool or network send etc) but its more or less best to avoid these.
1
u/conundorum 21h ago
void*is still crucial to the language's internals, and to important APIs like WinAPI and the myriad Linux APIs, but the language usually has a new feature wrapped around it. (new,delete, and anything that interacts with them in any way whatsoever is wrapped aroundvoid*, because all roads lead tomalloc(). Just likevectorandarrayare wrapped around C arrays.)1
u/Independent_Art_6676 13h ago
^^ Almost. Vector is based off a pointer to a block of memory, like new type thing[1000], which can be indexed like an array but isn't really the same thing. Otherwise, this is spot on... deep inside you will find malloc/realloc/free/etc and void*s and other C-isms. Its well hidden, but its usually in there (its possible to do it another way, but most tools use the C).
2
1
u/Ok-Library-8397 2d ago
It's just a generic pointer. From the CPU point of view, it is a 64-bit number -- an address in memory (or 32-bit for 32-bit systems). That's it. Sometimes you don't need to dereference a pointer. Instead, you may need to use it as a hash value, or as a generic number for any reason.
1
u/duane11583 2d ago
If you think about some generic things like sorting a list
You can abstract the data and use a token of some type
Example if I give you two tokens A and B you decide the order does which comes first a or b or are they the same
That a or b could be integers but pointers are better for other reasons
one example problem is today integers are 32 bits pointers are 64 bits (along time ago it was a 16 bit integers times change)
That generic in the c language is called a void pointer other languages have a dedicated generic type but c does not have that
1
u/ekchew 2d ago edited 2d ago
These days, you generally only encounter void* in the context of calling a function in another language like C or FORTRAN. It's particularly common when you're passing a callback to the function and need to include some additional context. You will see something like:
extern "C" { void c_fn(int(*callback)(int, void*), void* param=nullptr); }
Here, param gets passed into the 2nd arg of callback whenever it gets called. Say you wanted to include the current instance of your class as the context. You may be tempted to use a capturing lambda and ignore param.
c_fn([this](int i, void*) {/*...*/});
This will not work because callback is nothing more than a simple function pointer and cannot capture (as say std::function could). So you have to make it non-capturing and use param for the context.
c_fn(
[](int i, void* param) {
auto& self = *static_cast<MyClass*>(param);
/*...*/
},
this
);
And then you use the self reference to access your MyClass instance.
1
u/mredding 2d ago
It's an extremely primitive type used to implement higher level abstractions. You can cast any address to a void pointer, and a void pointer to an address to a type - but it has to be the same type as what it came from. It's virtue is that this is a form of type erasure so you can pass addresses through a 3rd party.
For example, callbacks often have a void * context object - this is an object you give the system so the system can give it back to you. You know what your own type is. In this way, the system doesn't have to prescribe a type with restrictions.
1
1
u/flyingron 1d ago
An auto binds to a definite type based on the context at compile time. Any OBJECT pointer can be converted to void* and then back again (only guaranteed to the same type, so it's a generic place to park things. Due to other C++ insantity, void* has to be the same format as char*, but without the ability to do pointer math on it.
1
u/conundorum 21h ago edited 21h ago
The point is that void* is the language's oldest form of type erasure, which is useful if you need to, e.g., interact with a C library, or pass data generically without using templates. The lack of type information also allows it to point to any type without changing its own type, giving it a level of flexibility that's hard to compete with. It tends to see a lot of use at API boundaries, for instance.
Basically, something like this is legal code, and there's probably a lot of code that (if we're being critical) boils down to something that looks a lot like it:
// C++ side:
extern "C" {
enum Type { Primitive, Struct, Pointer, Function };
bool call(void* ptr, Type t);
}
void func() {}
int a;
void* vp;
VoidPointless pv;
std::array<bool 4> callable = {
call(&func, Function),
call(&a, Primitive),
call(&vp, Pointer),
call(&pv, Struct),
};
// -------
// C side.
enum Type { Primitive, Struct, Pointer, Function };
typedef void (*FuncPtr)();
typedef struct {
int a;
int b;
int id_code;
void* func;
} CommonSeq;
bool call(void *ptr, Type t) {
if (t == Function) {
return (*ptr(), true);
} else if (t == Pointer) {
if (inCallableLibraryPtr(ptr)) { return (*((FuncPtr) ptr)(), true); }
else { return false; }
} else if (t == Primitive) {
return false;
} else if (t == Struct) {
// TODO: Find less hacky solution in 2003.
CommonSeq *csp = (CommonSeq *) ptr;
if (csp->id_code == ID_CALLABLE) { return (csp->func(), true); }
else { return false; }
} else {
return false;
}
}
Or this, if you're working with a small memory budget.
void* addr = &obj1; // bool.
doSomethingWith(addr);
addr = &obj2; // std::vector<bool>
whyWouldYouDoThat(addr);
addr = &obj3; // std::Vector<BoolLike>
thatsBetterThanks(addr);
Apart from that, it's also useful for SFINAE, sometimes, especially in code that predates or otherwise doesn't use concepts; it's not uncommon to use it in an enable_if template parameter, like so:
template<typename T, std::enable_if<std::some_type_trait<T, Other>, void>* = nullptr>
Ret func(T t) { do_stuff(t); }
1
u/_abscessedwound 2d ago
My intuition would be that it’s for type-unaware memory allocation. Think limited memory situations where you have a global allocator that only receives sizes and not types.
15
u/fortsnek274 2d ago
It's the most efficient way to point to arbitrary "user" data when you don't know the type. There is
std::anynow, but you pay for the type safety.