How to implement a hash table in C (2021)
31–40 of 43 posts
Re: How to implement a hash table in C (2021)
#32It's the ihih guy! Thanks for being one of the few C bloggers out there.
I enjoy reading posts about C just to see how much boilerplate and footguns higher languages let you avoid. I would like to write C, but I just have no trust I would remember or even know how to do things right (use `calloc` vs `malloc`, free the table when allocation of `entries` fails etc).
Whenever I write an allocation I try to write a free after it, then fill in the code in between.
Use of static analysers and sanitisers helps too.
Like some people enjoy making all their hand tools from scratch in their workshop, there's a craft and discipline to follow and a satisfaction in doing things well, I enjoy the craft of and discipline C for the same reason.
Re: How to implement a hash table in C (2021)
#33There was a fantastic benchmark of C and C++ hash tables doing the rounds a few weeks ago, it's pretty fun reading: https://jacksonallan.github.io/c_cpp_hash_tables_benchmark/ . Unless I really didn't want to introduce dependencies, or reduce code size, I think I'd use an off the shelf hash table implementation these days. It's still a fun exercise building your own though.
One important consideration is missing from this benchmark: how they behave with multi-threading. There can be big differences between diffent hash table implementation when you have to use them from different threads (built-in smart thread safety vs external dumb locks, etc)
Re: How to implement a hash table in C (2021)
#34> but it is non-ideal that I’m only allowing half the range of size_t. I am fairly certain that in C it's actually impossible to have an object whose size is larger than half of the range of size_t unless ptrdiff_t is wider than size_t, which normally isn't. Unless, of course, C standard decided to make subtracting two valid pointers into the same array (or one past the end) a potential UB, just because.
> If the result is not representable in an object of that type, the behavior is undefined. In other words, if the expressions P and Q point to, respectively, the i-th and j-th elements of an array object, the expression (P)-(Q) has the value i - j provided the value fits in an object of type ptrdiff_t.
Re: How to implement a hash table in C (2021)
#35> but it is non-ideal that I’m only allowing half the range of size_t. I am fairly certain that in C it's actually impossible to have an object whose size is larger than half of the range of size_t unless ptrdiff_t is wider than size_t, which normally isn't. Unless, of course, C standard decided to make subtracting two valid pointers into the same array (or one past the end) a potential UB, just because.
Actually, that is UB. > If the result is not representable in an object of that type, the behavior is undefined. In other words, if the expressions P and Q point to, respectively, the i-th and j-th elements of an array object, the expression (P)-(Q) has the value i - j provided the value fits in an object of type ptrdiff_t.
Re: How to implement a hash table in C (2021)
#36Earlier quoted context omitted.
Actually, that is UB. > If the result is not representable in an object of that type, the behavior is undefined. In other words, if the expressions P and Q point to, respectively, the i-th and j-th elements of an array object, the expression (P)-(Q) has the value i - j provided the value fits in an object of type ptrdiff_t.
Although, I can't help thinking you're going to run into bigger problems than that if the size of your buffer has grown to 9,223,372,036,854,775,807 bytes.
This trade-off is actually making me sad. One can either have all the sizes and offsets fit into (unsigned/signed) integers, or one can have objects spanning more than half of the memory space. I think for my language I'd pick "all sizes/offsets are signed, and so yeah, you can't span all of the memory with a single char array".
[0] https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2808.htm
Re: How to implement a hash table in C (2021)
#37Re: How to implement a hash table in C (2021)
#38I played around with C++ when I was at university. Then never touched it again. So, with a grin I stumble over things like "void* ht_get(...)" Wait. What? A void pointer? Interesting... I have no clue. I like articles like these. For someone not familiar with C it's a perfect level. In terms of explanation and the code itself.
I remember back in the late 90s to early 00s, I had fun countering OOP guys claiming C "cannot do XYZ" - but it obviously can.. the C way.
For method override, my mind was blown when I discovered function pointers!
It was my "Wait. What?" moment.. along with various other things!
Below is sample code (not tested)
// generic monster "class"
struct monster_t {
int health;
struct weapon_t *weapon;
// etc..
void (*attack)(struct monster_t *self); // our function pointer
// more function pointers for update, die, hit, etc...
};
// impl
void monster_notassigned_attach(struct monster_t *monster) { // safety net (todo)
printf("Attack not assigned to monster...\n");
}
void monster_dragon_attack(struct monster_t *monster) {
// attack for dragon
printf("Dragon is attacking you...\n");
}
void monster_wizard_attack(struct monster_t *monster) {
// attack for wizard
printf("Wizard is attacking you...\n");
}
// creation
void monster_dragon_create(struct monster_t *monster) {
// allocate, then assign.. and do...
monster->attack = monster_dragon_attack;
}
void monster_wizard_create(struct monster_t *monster) {
// allocate, then assign.. and do...
monster->attack = monster_wizard_attack;
}
void main() {
// testing...
struct monster_t monsters[10]; // can store various monsters!
monster_dragon_create(&monsters[0]);
monster_wizard_create(&monsters[1]);
for(int i = 0; i
Not suggesting I support or follow the above especially for games. It is just a proof of concept!
I always tell people that coding in C (or any non-OOP language) should not be done the way you would in Java, C#, etc. As mentioned, the above is a demonstration.Re: How to implement a hash table in C (2021)
#39Earlier quoted context omitted.
Although, I can't help thinking you're going to run into bigger problems than that if the size of your buffer has grown to 9,223,372,036,854,775,807 bytes.
Well, but what if you're working with a 16-bit microcontroller? Apparently, there were quite some heated discussions about that which is why, while size_t was required to be at least 16 bits, ptrdiff_t was required to be at least 17 bits, in C99... and then it was reverted back to 16 bits in C23. See e.g. [0] This trade-off is actually making me sad. One can either have all the sizes and offsets fit into (unsigned/si…
... although I can't help thinking you have larger problems if you have two objects larger than 16k in the same array in a machine that has at most 64k of memory. :-P Or a char array greater than 32k for that matter.
I do appreciate the theoretical horror, but thank you anyway, C23! Having to cast your size_t's to ptrdiff_t's before you subtract them in order to preserve portability seems ... impractical.
Re: How to implement a hash table in C (2021)
#40Earlier quoted context omitted.
Well, but what if you're working with a 16-bit microcontroller? Apparently, there were quite some heated discussions about that which is why, while size_t was required to be at least 16 bits, ptrdiff_t was required to be at least 17 bits, in C99... and then it was reverted back to 16 bits in C23. See e.g. [0] This trade-off is actually making me sad. One can either have all the sizes and offsets fit into (unsigned/si…
Interesting. I hadn't thought of that. ... although I can't help thinking you have larger problems if you have two objects larger than 16k in the same array in a machine that has at most 64k of memory. :-P Or a char array greater than 32k for that matter. I do appreciate the theoretical horror, but thank you anyway, C23! Having to cast your size_t's to ptrdiff_t's before you subtract them in order to preserve portabi…