These days, I would mainly (95%) allocate all fixed-size variables, arrays, etc at compile-time. And only malloc stuff that needs to be sized at run-time. I just find it's easier. And I'm lazy: I don't waste time optimising stuff that's not critical.
But, once upon a time, RAM was horribly expensive ($25 per KILObyte in my own experience) and it made a lot of sense to malloc most the structs, arrays, etc. However, those days are long gone and who cares if my program takes up a few more tens of MEGAbytes on startup. Most of that will be paged out by the operating system, anyway.
Most initialisation is to (belt and braces) zero stuff out at compile-time, even if the compiler will zero it out anyway. Heap stuff will be zeroed out specifically also when invoking the function - function variables will be set to specific values, usually zero. ("int x = 0;")
Large arrays and structs that require 'set and forget' initialisation such as tables, binary-blobs, strings, etc are handled at compile time by using header files.