0

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?

TomSawyer
  • 3,711
  • 6
  • 44
  • 79
  • I do not no nothing about Go but in C any pointer is an unsigned integer (as it's a memory address). The pointer type represent the type of the variable that is pointed, and not the pointer itself. – Welgriv Apr 28 '20 at 16:16

1 Answers1

3

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.

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • i updated the code with usage of above func `uint64_t *table = (uint64_t *)hashtable_of(ht);` it casts `void *` into `uint64_t`, i still don't understand how `data` can be cast to `unsigned char pointer` , and what's the goal of that func – TomSawyer Apr 28 '20 at 17:06
  • 1
    @TomSawyer Detail: That is not a cast from `void *` to `uint64_t` (an integer). It is a cast of a `void *` to `uint64_t *`, a pointer to another kind of pointer. The goal of the function is to retrieve the address of data (rather than the data itself) stored in a hashtable and present it as a `void*` for subsequent assignment to various pointer types. – chux - Reinstate Monica Apr 28 '20 at 17:21
  • @TomSawyer Difficult to fully say for certain as only snippets of code are here, but `data` in `struct HashTbl` is more likely a pointer to the _data_, rather than the data itself. C passes pointers to data around a lot, rather than the _data_. – chux - Reinstate Monica Apr 28 '20 at 17:25