Why is it ok to code in C as if allocations of automatic
storage will never fail, but not ok to code as if
allocations of allocated storage will never fail, when
in fact both might fail?
The answer to this is multi faceted, and not entirely satisfactory. It
can be summarized by these points:
- malloc can potentially give you access to the full memory resources
of the system, whereas stack space is ``artificially'' limited.
Hence, a malloc failure is an environmental condition, whereas
hitting a stack problem is regarded as an internal problem in
the program. (C programmers are supposed to understand that
stack space is limited; often, the maximum automatic storage
a program can have is nowhere near the amount of dynamic memory
it can ahve).
- It's not unreasonable for programs that handle input of size N to
allocate O(N) dynamic storage. The same cannot be argued for
programs that require (or at least typically require) O(N) automatic
storage (or worse) for input sizes of N; such programs are
incompetently designed, or else, at best, have some pathological
behavior which ought be documented so it can be avoided.
Programmers shoudl study computer science and take steps to avoid
blowing stacks: use tail recursion, or else use divide-and-conquer
type recursion that needs O(log N) space.
- The cause for running out of malloc space may be external; not
attributable to the program which is affected by the failure.
For instance other programs may have gobbled up space.
The cause for blowing a stack is attributable solely to the program.
(But note: in virtual memory systems, both malloc space and the stack
may be lazily allocated, and under overcommit, the program can die
when accessing either malloc space or stack with no detection;
not discussing that configuration here).
- on a note related to the previous point, malloc failures are
recoverable, at least conceptually. It may be the case that system
resources can be freed or added to allow a re-try to succeed. On some
systems we can dynamically add a swap file, increasing the amount of
virtual memory. Even RAM might be hot-swappable. Or we can shut down
some programs. Perhaps we can even dynamically migrate the program to
another compute node which has more local memory available. Recovery
strategies don't make sense with respect to a blown stack: a blown
stack is not OOM (a point raised in bullet #1 above) and so liberating
memory won't help.
- one program module A can cause a malloc failure in another B by
allocating a lot of memory. The failure in B will happen even if A is
not a caller. A and B can be totally unrelated. If a program module A
contributes to a blown stack in B, it must be the case that A is a
direct or indirect caller of B. I.e. functions have to ``conspire''
to blow the stack, so to speak, at least to a greater degree.
- checking for stack allocation failures would be terribly inconvenient;
so much so that if such a thing is provided, it practically requires
exception handling, so that the source program doesn't have to express
the checks, and so that the checking is consistently applied.
Automatic storage is allocated whenever function calls are opened and
block scopes entered. Checking for out-of-stack space at all these
places would be cumbersome, and would not provide any value to
well-behaved programs: well-behaved (from a stack viewpoint) programs
would only look badly written, for not doing the checks. Programmers
writing good code that uses a modest amount of stack space would have
to defend their code against stupid, unfounded accusations of
unreliablity due to missing checks. Myopic project managers would
write coding guidelines calling for these checks to be done
everywhere.
These kinds of checks have to be done 100% consistently everywhere or
not at all. If the 99 stack frames all did the check, but the 100th
nested one does not, and is the one that blows the stack, those 99
checks are for nothing.
Be some of it right or wrong, I think that the above reflects some of
the common reasoning why we get away without checking automatic storage
allocations.