Tomás Ó hÉilidhe said:
I'm currently writing a program that contains a large static-duration
object, about 240 kilobytes in size, that I use for storing
information gathered at runtime.
I've been playing around with GCC and I found something interesting.
If you compile the following:
int arr[999999];
int main(void)
{
return 0;
}
Then you get a small executable. Similarly you get a small executable
for the following:
int arr[999999] = { 0,0,0,0,0,0,0 };
However, if you set even one of the elements to something other than
zero, then you end up with a big executable:
int arr[999999] = { 1 };
So of course you'd be better off to do:
int arr[999999];
arr[0] = 1;
Right. At least on Unix, the executable format includes (among others)
two different "sections" for data, one called .data and the other called
..bss. The initial contents of .data are loaded from the executable,
while .bss is initialized to zero by the operating system. So the
executable needs to contain the whole contents of .data, but only a
size for .bss.
Naturally the compiler will arrange that any object that's initialized
to all zeros goes in the .bss section, including objects of static
duration which get the default zero initialization. (This works best
when NULL pointers and floating-point zeros are also all zero, which is
typically true on Unix systems.) Any object that isn't completely
initialized to zero, like in your example, has to go in .data, which
means the whole thing gets stored in the executable, even if most of
that happens to be zeros.
You can certainly imagine an optimization like the one you mention,
where an object that's mostly zero could go in .bss and have the nonzero
parts filled in automatically as the program starts. This turns out to
be tricky to implement. You might imagine that the initialization code
could be invisibly inserted at the beginning of `main', but this is hard
to do if the object is defined in a different source file from `main'
which is compiled separately and linked later. One option might be for
the compiler to take advantage of the "constructor" features that the
linker usually supplies to support C++, where static-duration objects
may have constructors that need to run before `main'. But there may be
code that runs earlier still that needs the values of initialized
static-duration objects to be correct: the library's startup code, or
the programmer taking advantage of the same "constructor" feature, or
doing some other system-specific magic.
So in short, you might be able to make this optimization work if you
only needed to support ISO C programs. But real compilers generally
prefer to allow programmers to take advantage of non-portable features
of the system, and that makes it much harder to get this optimization
right. So therefore it's normally not attempted. If you want it
done, you need to do it yourself, and that leaves it up to you to make
sure the initialization is done at a time that works for you.