Why not provide both, to get the best of both worlds?
Use _init and _terminate functions to use method #1 (or whatever naming you see fit).
Use additional _create and _destroy functions for the dynamic allocation. Since _init and _terminate already exist, it effectively boils down to:
myStruct *myStruct_create ()
{
myStruct *s = malloc(sizeof(*s));
if (s)
{
myStruct_init(s);
}
return (s);
}
void myStruct_destroy (myStruct *s)
{
myStruct_terminate(s);
free(s);
}
If you want it to be opaque, then make _init and _terminate static
and do not expose them in the API, only provide _create and _destroy. If you need other allocations, e.g. with a given callback, provide another set of functions for this, e.g. _createcalled, _destroycalled.
The important thing is to keep track of the allocations, but you have to do this anyway. You must always use the counterpart of the used allocator for deallocation.