Search code examples
cpointerscastingmmapvoid-pointers

Mmap, void pointer and type casting in C


I'm reading C on disk hash table code without knowing much about C or mmap but i know Golang.

This piece of code confuses me. There are two structs like this.

typedef struct HashTbl
{
    void *data;
    ...
} HashTbl;
typedef struct Header
{
    char magic[16];
    size_t total;
    size_t used;
} Header;

It uses mmap to map HashTbl data property

ht->data = mmap(NULL, data_size, prot, MAP_SHARED, file, 0);

ht type is HashTbl, ht->data would be cast to Header to set property value like this:

Header *h = (Header *)ht->data;
strcpy(h->magic, MAGIC_STR);
h->total = 12;
h->used = 0;

Then this function:

void *hashtable_of(HashTbl *ht)
{
    return (unsigned char *)ht->data + sizeof(Header);
}

usage of this function:

uint64_t *table = (uint64_t *)hashtable_of(ht);

I don't understand what's the purpose of this function, is that to calculate the length of void pointer (Header::data) value?

void pointer in C seems like interface{} in Go, which could be cast to any type. but Go has error handling while doing type casting, if we cast interface{} type to wrong type, it would return the error

But in this C code, it casts a Struct -> unsigned char pointer and combine it to sizeof other struct, which means unsigned char pointer is an integer?!

How is that even possible?


Solution

  • it casts a Struct -> unsigned char pointer and combine it to sizeof other struct, which means unsigned char pointer is an integer?

    void *hashtable_of(HashTbl *ht)
    {
        return (unsigned char *)ht->data + sizeof(Header);
    }
    

    Not quite. Code is starting with one pointer, ht and determining another pointer, the return pointer.

    There is not cast of a struct. There is a cast of a unsigned char *.

    There are no integers in this code aside from sizeof(Header).


    Let us take it in steps:

    The pointer ht has member .data which is de-referenced by ht->data. That results in a void *.

    The void * is cast to an unsigned char pointer.

    Next, code performs pointer addition with ... + sizeof(Header). Pointer addition is like integer addition, yet has differences. Here the addition of a pointer and integer results in another unsigned char pointer that is sizeof(Header) bytes (unsigned char) further along in memory.

    Lastly this unsigned char pointer is converted to a void * as part of the return.


    hashtable_of() overall usage is unclear without the surrounding code.


    void pointer in C seems like interface{} in Go, which could be cast to any type.

    Almost. A void pointer can be cast to any object pointer with restrictions of value validity and alignment. A void pointer may be insufficient to represent a function pointer though. C lacks a 100% portable universal pointer.