C
Chris Torek
For uninitialized global variables, the loader(LD) allocates data segment
memory using calloc() which does the "initialization" to NULL for all
elements ...
This is not in fact how it is implemented on Unix-like systems with
an "ld" linker or equivalent (gld). There is no call to calloc()
involved. The symbols are simply managed in a table until it is
time to assign locations to them; if they are still marked "bss"
at that point, they typically go into a "bss segment" (whose details
are file-format dependent -- a.out, COFF, and ELF are all at least
slightly different here; and some COFF and ELF formats also offer
a "small data segment" area into which small variables are collected
for register-offset addressing in the runtime image).
All of these details are irrelevant to portable C programs, which
need only concern themselves with the fact that static-duration
objects are initialized, as if by an explicit "= { 0 }". (Note
that braces are allowed for scalars; 0 suffices to set a double to
0.0 or a pointer to NULL; and only the first element of any aggregate
need be initialized to cause the rest to be initialized. Hence,
regardless of the object type to which the letter T is mapped, the
line:
T var = { 0 };
is always valid in C. If T is a function type, any initialization
is invalid, of course.)
So ...
struct {
int ii;
long ll;
char a[32];
} gvar;
creates an entry in the disk program image that the compiler emits to disk
that informs the loader to allocate 1 element of sizeof(gvar) memory.
In general, "gvar" in this case will be in a bss segment, along
with all other bss variables. When you actually run the binary,
the OS does not allocate one small chunk of memory at a time.
Instead, one or more entire pages or segments are allocated (or
marked for zero-fill, on a paging OS) based on the *total* size
given in the bss segment. This is quite OS-dependent, of course,
and assumes that the machine's all-bits-zero is the correct way
to initialize "double"s and pointers and so on.
Memory "allocated" between braces "{}" is actually stack memory
which retains the data from the last function to use that stack
memory. This memory can/will change from call to call being
altered by the "other" functions that are called in between.
It took me quite a while to figure out what you meant by this. As
stated, this is wrong, because it claims that, in:
void f(void) {
static int x;
...
}
x will be "stack memory". In fact, x is yet another "bss segment"
(or, potentially, small-data-segment) variable. The critical thing
is not the braces, but the fact that x has static storage duration.
If x had automatic storage duration -- which is indeed the default
for block-scope variables -- it would, as you say, typically be
stored on a (usually single, often OS-provided) stack.
Note however that IBM S/370 C compilers allocate "stack frames"
using the same, general-purpose "heap" memory that malloc() and
company use. The system does not provide, nor use, a stack: it
simply builds a LIFO data structure that *acts as* a stack (but is
not a single contiguous area, unlike typical OS-provided stacks).
(The S/370 approach has some big advantages when dealing with
languages that use coroutines.)