Rust sounds like what you want, though be prepared to deal with near C++ levels of complexity at times (lots of Rust code is too macro happy for my tastes).
That said, the way to deal with memory management in C is to... not do much of it. I know that sounds like a cop-out but the patterns you're supposed to use in languages like Zig and Odin are the same ones you'd use in C to keep your mind sane.
Do not do Reference Counting or Single Owner + Borrowing (RAII), it'll drive you insane without the automation languages like Swift and Rust or C++ give you.
Instead, the way you're supposed to do things, are big "manager objects". Only these manager objects are ever explicitly allocated and freed, everything stored within them is managed by them and they expose only safe handles to the outside world (e.g. generational handles).
Most of the time these manager objects will store some dynamic arrays, hash maps or pools within them that get freed when they are also freed. Note that unlike RAII there is no real "nesting". The managers are responsible for the lifetimes of their whole "object tree".
Any temporary data should be allocated using a temporary allocator (like an Arena allocator) which gets freed at the appropriate place for the application (end of a frame in a game, or end of a request in a web server). Never store pointers to something within the temporary allocator in "long lived" data structures inside the manager object.
Follow these rules and things get manageable, Zig and Odin have lots of facilities in their standard libraries to make this easier, in C you're mostly on your own but there are some libraries you could use like the Apache Portable Runtime.