Question says it all but here is an example:
typedef struct mutable_t{
int count, max;
void **data;
} mutable_t;
void pushMutable(mutable_t *m, voi
Find out how the application framework handles an OOM. Many will simply not handle an OOM. Most of the time a framework will not operate properly in no-free-RAM conditions unless it says very clearly and unambiguously somewhere that it will. If the framework won't handle an OOM and is multithreaded (many are nowadays), an OOM is gonna be the end of the show for the process in a lot of cases. Even if it isn't multithreaded it may still be close to collapse. Whether you exit the process or the framework does may be a moot point; a predictable immediate exit may just be a bit better than a crash out at some semi-random point in the near future.
If you're using a separate special-purpose sub-memory pool (ie not your usual malloc) for a well-defined set of operations that are only constrained in memory use by OOM (ie the current operation is rolled back or aborted cleanly on OOM for the sub-memory pool, not the whole process or main memory pool), and that sub-pool is not also used by the application framework, or if your framework and the WHOLE of the rest of the application is designed to maintain meaningful state and continued operation in no-free-RAM conditions (rare but not unheard of in kernel mode and some types of systems programming) you may be right to return an error code rather than crash the process.
Ideally the bulk of the memory allocations (or even more ideally all the allocations) for a piece of processing should be allocated as soon as possible in processing, ideally before it properly begins, to minimise the problems of data integrity loss and/or amount of rollback coding required if it fails. In practice a lot of the time, to save programming cost and time on projects, to preserve data integrity applications rely on database transactions and requiring the user/support person to detect a GUI crash (or server crash) and restart the app when out of memory errors occur, rather than being written to cope with and rollback on any and all of thousands of potential OOM situations in the best possible ways. Then efforts focus on trying to limit the exposure of the app to overloading situations, which may include additional validation and limits on data size and simultaneous connections and queries.
Even if you check how much memory is reported as available, often other code may alloc or free memory as you do, changing the basis for your memory check and possibly leading to OOM. So checking available free RAM before you alloc is often not a reliable solution to the problem of making sure your application operates within available RAM limits and maintains data integrity enough of the time to satisfy the users.
The best situation to be in is to know how much memory your app requires in all possible cases, including any framework overheads, and to keep that figure within the amount of RAM available to your application, but systems are often so complicated with external dependencies dictating data size so achieving this can be unrealistic.
The acid test of course is are you satisfying the users sufficiently through high up-time, and infrequent data corruption, loss or crashes. In some cases an app having a monitor process to restart it if it crashes is useful.
As regards realloc:
Check the return value from realloc - put it in a temporary variable. Only care if it is NULL if the new size requested was >0. In other cases place it in your non-temporary variable:
eg
void* temp = realloc(m->data, m->max * sizeof(void*));
if (m->max!=0&&temp==NULL) { /* crash or return error */ }
m->data =(void**)temp;
EDIT
Changed "most cases" to "a lot of cases" in (1).
I recognise that you said to assume that "something can be done" if the memory cannot be allocated. But memory management is a very global consideration (!).