i'd recommend embracing hungarian naming (if you call it apps-hungarian you've not embraced it fully enough, read older documentation) and be as rigid as rust about using it. i-, c-, p-, and Max are your best friends, don't declare one of those without declaring all the others that you will need.
using hungarian, adopt naming conventions about Open/Close, Init/Finish, Alloc/Free, Start/End, Alpha/Omega, or whatever, and apply them rigidly to any class/struct (with indentation that's obvious). When you put in an Open, you go and put the Close in at that moment, just as you would close any paren you opened. don't return from all over the place in a function, goto endblock and free what needs freeing, you need to work on autopilot not thinking through the twists and turns to play monte carlo with getting it right. If you are going to return an allocation, your name needs to be Open, Init, etc. Use whatever words you want, but it's got to be something that makes you as the caller think "this is an open paren, i need to close it"
using ifdef debug type mechanisms, hook malloc and free and put guard word asserts at the beginning and end of every allocation, and magic number ids and reference counts in all structs, and hook main() and exit() so you check those for leaks. all. the. time.
work in a systematic way that avoids problems and make that your priority, everything else like functionality is slaved to that.