Yeah C doesn't really go in for that sort of thing. The standard library tends to be much more about some minimal support for strings and interfaces to OS features like files, signals, memory allocation etc. It doesn't really provide much in the way of building blocks to be reused by application developers.
The recommendation out there on the net seems to be to look at Glib, which is used by gtk, for that sort of thing.
Another good alternative might be the NSPR - https://firefox-source-docs.mozilla.org/nspr/reference/index...
I used this way back in 2001-3 for a multi-platform project because it provides some good platform abstractions, and it looks like it has a hash-table implementation in amongst its other features.
Development in C is slow, fraught with unexpected pitfalls and you end up writing an awful lot of stuff from scratch. While this is satisfying in some ways, I find the modern paradigms of the languages I work in now to be more fulfilling - yes you throw a lot of existing components together, but you also get to deliver functionality much more frequently.
There are also a lot of very old-school C shops out there, that don't believe in modern things like CI/CD, git, even automated testing. I'm sure there are good ones too, but there are a lot of dinosaurs in that arena. One of the last ones I contracted for (for about three weeks until I quit) responded to my usual first-day question of "OK, how do we build this product?" with "Oh don't worry about that, we can sort that out later" and later never came.
That all said - I really enjoyed working on a couple of embedded devices. Seeing what you can achieve with 128kB of SRAM and 256kB of flash is challenging, and since I was a kid I've enjoyed making the computer do stuff. With embedded devices that have buzzers, leds, little displays etc, you get immediate feedback. And having to think more about things like memory allocation strategies does influence your thinking in (I think) a good way. You can definitely gain some deep knowledge!