Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

casting pointer to pointer... to pointer?

I found this snippet

void* operator new(size_t nbytes)
{
  if (nbytes == 0)
    nbytes = 1;                    // so all alloc's get a distinct address
  void* ans = malloc(nbytes + 4);  // overallocate by 4 bytes
  *(Pool**)ans = NULL;             // use NULL in the global new
  return (char*)ans + 4;           // don't let users see the Pool*
}

here https://isocpp.org/wiki/faq/dtors

I spent over an hour now trying t understand what *(Pool**)ans = NULL; does. ans is a void pointer, so I would assume it is cast to a Pool pointer and the pool is set to 0. Not he pointer but the pool itself, because of the third * on the left. But Pool has no operator= defined.

pointer** in a declaration is apparently a pointer to a pointer... but in this context this makes no sense to me, as ans is a single pointer.

like image 734
lo tolmencre Avatar asked Dec 24 '22 01:12

lo tolmencre


1 Answers

The only reason to use Pool** here is semantic correctness, since presumably that "hidden" 4-byte header is supposed to be a pointer to a Pool (so ans is a pointer to a pointer to a Pool, and *(Pool **)ans has type Pool *).

You couldn't do *(Pool *)ans = NULL unless you were able to assign a Pool to NULL, and that is probably not the intended effect here anyways. Something like *(int **)ans = NULL or the more ridiculous *(Pool ******)ans = NULL would've had the same end effect but would have been semantically odd if it is ultimately intended to be a pointer to a Pool.

At the end of the day you'll end up with:

 +---+---+---+---+- - -
 | 0 | 0 | 0 | 0 | ... and nbytes more bytes
 +---+---+---+---+- - -

 ^ ans           ^ returned address (ans + 4)

Where those first 4 bytes are intended to be a pointer to a Pool somewhere.

Another way to think about this is to ignore the whole nbytes thing, consider this general pattern:

void * ptr = malloc(sizeof(TYPE));
*(TYPE *)ptr = VALUE;

That should make sense. Now then if TYPE is a Pool * and VALUE is NULL and you fit it into that pattern, you can see how it all makes sense still:

void * ptr = malloc(sizeof(Pool *));
*(Pool **)ptr = NULL;

And then in your case you're still basically doing that although you're allocating some extra bytes at the end, but that's irrelevant to the types here.

As an aside, it's potentially asking for trouble here (and also semantically lazy) to hard-code 4 everywhere instead of sizeof(Pool *).

like image 98
Jason C Avatar answered Dec 26 '22 15:12

Jason C