0

I'm confused about void pointers. Although this code fragment compiles, is dereferencing variable c in function f2 undefined behaviour?

#include <cstdlib>
#include <cstdio>

void f2(void* c) {  // legal/stupid
    // although this compiles, is dereferencing c here undefined behaviour?
}

void f1(void** b) {
    printf("%d\n", *(int*)*b);
    f2(b);
}

int main() {    
    int i{ 5 };
    void* a{ (void*)&i };
    f1(&a);
    return 0;
}
sid
  • 157
  • 8
  • 2
    Any particular reason for initializing your variables with curly braces? Also, it would be nice to include in the question the reason for which you think this code invokes UB... :) – gsamaras Aug 05 '19 at 19:23
  • You can't dereference a void pointer. –  Aug 05 '19 at 19:24
  • 2
    Unless you have to work with C API's, I'd suggest you stay away from void pointers. C++ has a very good type system and templates replace the need for void pointers. – NathanOliver Aug 05 '19 at 19:24
  • @NeilButterworth is [right](https://stackoverflow.com/questions/15468441/dereference-void-pointer), but I think OP didn't not mean to ask that.. I guess.. :) – gsamaras Aug 05 '19 at 19:25
  • @NeilButterworth The OP isn't. A `void**` is not a `void*`, it is a pointer to a `void*` and you can dereference that, as it yields a `void*`. – NathanOliver Aug 05 '19 at 19:29
  • @Nathan I was responding to the comment `// although this compiles, is dereferencing c here undefined behaviour?` As `c` is a void pointer, you cannot dereference it. –  Aug 05 '19 at 19:32
  • @NeilButterworth Oh, OK. Missed the comment. – NathanOliver Aug 05 '19 at 19:32
  • 1
    _@sid_ Any reasoning why you're messing around with raw pointers in c++? – πάντα ῥεῖ Aug 05 '19 at 19:37
  • Note that `void**` arguments in C APIs are only used for output parameters that are pointers of unknown type. An example is `posix_memalign()`, which returns an error value and sets the `void*` whose address you pass in to the allocated block of memory. There is basically no reason to ever use `void**` in C++, other than compatibility with these APIs. – Davislor Aug 05 '19 at 19:40
  • Dereferencing `c` will throw an error: "'void*' is not a pointer-to-object type'". In general, you can't dereference something into `void *` (note that you can still dereference void** as you did in `f1`, think of that as if the complier gives you a last chance to cast to a concrete type). You can still cast c to the desired pointer, and then dereference it. – Eliran Abdoo Aug 05 '19 at 19:40
  • There's almost *never* a reason to use a `void*` in C++. And this doesn't look like one. – Jesper Juhl Aug 05 '19 at 19:48

2 Answers2

1

Converting from a void* back to a pointer of the original type is not undefined behavior:

int a = 10;

void* b = (void*)&a;

int* c = (int*)b;

int d = *c; //d == 10 now; this is fine

So, if f2 cast c back to a void** before using it, the code would be well-defined. If f2 tries to cast c to something else (for example, if f2 cast c to an int* even though it was given a void**), that would be undefined behavior.

Pretty much the only exception to this is casting to char*: that's guaranteed to be ok.

That being said, because you have no type safety when using void* pointers, if at all possible you should use something else.

This means that this:

void f2(void* c) {
    void** b = (void**)c;         // Get original void**
    void* b_value = *b;           // Dereference to get void*
    int* int_ptr = (int*)b_value; // Get int* pointer

    int value = *int_ptr;         // Use it
}

Is safe, if and only if c represents a pointer to a void* which itself is a int*.

Alecto Irene Perez
  • 10,321
  • 23
  • 46
0

Your code causes undefined behavior.

Below fragment is taken from C11from chapter §6.2.5:

The void type comprises an empty set of values; it is an incomplete object type that cannot be completed.

This means that a pointer to void is an invalid operand for dereferencing.

bogdan tudose
  • 1,064
  • 1
  • 9
  • 21