Search code examples
cpointerscastingfunction-pointersdereference

What type of cast does *( char** ) achieve?


What type of cast does *( char** ) accomplish?
It seems to create a pointer to a pointer ("double pointer") with the **. But the * outside the parentheses confuses me... Below is the cast in context:

void mem_test ( void )
{
    void* m1;
    void* m2;

    ...

    m1 = 0;

    while ( ( m2 = malloc( 10001 ) ) != 0 )
    {
        *( char** ) m2 = m1;  // what does this cast mean?

        m1 = m2;
    }

    ...
}

Another confusing variant of it appears in the following snippet. What does the cast accomplish in this scenario, and is it different from the one above?

void mpenter ( void );  // a function with no parameters and no return value

...

uchar* code;

...

*( void ( ** ) ( void ) ) ( code - 8 ) = mpenter;  // what does this cast mean?

What is the proper name for this type of cast? I.e. what should I be searching for to learn more about it?


Solution

  • mem_test

    Let's dissect this step-by-step. To make it easy, assume we're on a 64-bit architecture where pointers are 8 bytes in size.

    • m2 = malloc( 10001 ) assigns m2 to a freshly allocated block of memory.
    • (char **) m2 says "let's pretend the first 8 bytes of memory at address m2 is a char*, and point at that -- resulting in a char**"
    • *(char **) m2 dereferences the above, resulting in an l-value to a char* (in C speak). Remember l-values are writable.
      • in C++, it would result in a reference to a char*, i.e. char*&
    • *(char **) m2 = m1; writes the value of m1 into the l-value created above.

    Another way to write that code would be:

    char* pCurrentBuffer;
    char* pPreviousBuffer;
    
    pPreviousBuffer = NULL;
    while ( ( pCurrentBuffer = malloc( 10001 ) ) != NULL )
    {
        char** ppPreviousBuffer = (char**)pCurrentBuffer;
        *ppPreviousBuffer = pPreviousBuffer;
    
        pPreviousBuffer = pCurrentBuffer;
    }
    

    Surprise(!), in the end, it creates a linked list. (technically it's a reversed linked-list because they are all previous pointers instead of next pointers, but I am joking)

    mpenter

    This is casting the 8 bytes at address code - 8 to a pointer-to-function-pointer, so that it can assign the address of function mpenter into that location.

    • void (*)(void) is a pointer to a function that takes no parameters and returns void.
    • By extension, void (**)(void) is a pointer to a function-pointer of the above type.
    • Dereferencing that yields an l-value to a function-pointer.
      • In C++ it would create a reference to a function-pointer, if that's easier to reason about.
    • Assigning it writes the address of mpenter into that l-value (i.e. the 8 bytes of memory at address code - 8)

    It's unusual to take a negative offset from a pointer, but if code is pointing into the middle of a struct of some kind, it may be legitimate (in some loose sense of the word).