foo f;
foo_init(&f);
foo_destroy(&f);
...
foo *g = malloc(sizeof(foo));
foo_init(g);
foo_destroy(g);
free(g); foo f;
foo_init(&f);
foo_destroy(&f);
...
foo *g = malloc(sizeof(foo));
foo_init(g);
foo_destroy(g);
free(g);I would write the allocation as
foo * const g = malloc(sizeof *g);
to avoid repeating the type name and "lock" the allocation to the variable. If the type on the left hand side ever would change, this still does the right thing.In the case you show, foo would have to be a struct that doesn't contain pointers to additional allocated memory, but its a entirely valid use case and pattern.
Calling malloc and free, has a cost associated with it, that the stack doesn't. But stack can in some cases, like with recursion be scary to use, because you dont know where it ends. If malloc returns NULL you know you have found the end and can do something reasonable.
Thanks for you insight!
In GPU programming, there are actual different memory sub-systems, with different sizes and different performance implications, so it’s critical that the caller is able to do the allocation & deallocation any way they want. This is why most well designed GPU APIs rarely allocate GPU memory inside the API, but instead are designed to work with caller-provided pointers to buffers.