And the OP is right - globals, especially if not encapsulated within a single code module, are a prime cause of opaque spaghetti code.
And the OP is right - globals, especially if not encapsulated within a single code module, are a prime cause of opaque spaghetti code.
I guess it depends on what exactly you're working on, but passing in a stack-declared for a function to fill with its results seems better to me anyway, as general practice. Preferable to allocating a new structure in the function and then returning it, that is. There are no ownership issues and nobody has to remember to free anything.
Heap emulation with static buffers comes with its own costs - either you end up writing an allocation engine or you just use a large buffer with an advancing pointer, free() does nothing and the whole thing has to be reset every so often, which means that you have to convince the other developers on the team that it's not to be used like normal malloc and please be careful and ... that's a whole other can of worms!
So I made once had the displeasure of doubling the performance of a fission reactor safety simulator by disabling win32's swap... 30 years and 2e7 LoC codebase. >.<. The working set was nowhere near smart, but it did work faster than AIX, HP-UX, SCO & Solaris/SunOS ports.