As per the C language spec., uninitialized globals are initialized to zero (0). Nandu310 tells us why on his blog about the memory areas in the C language.

Data segment: the data segment is to hold the value of those variables that need to be available throughout the life time of the program. [...]

There are two parts in this segment. The initialized data segment and uninitialized data segment. When variables are initialized to some value (other than 0 or which is different value), they are allocated in the initialized segment (.data). When the variables are uninitialized they get allocated in the uninitialized data segment (.bss). [...] [The] job of initialization of variables to zero is left to the operating system to take care of. This bulk initialization can greatly reduce the time required to load.

When we want to run an executable program, the OS starts a program known as loader. When this loads the file into memory, it takes the BSS segment and initializes the whole thing to zeros. That is why the uninitialized global data and static data always get the default value of zero.

Nandu310 explains the segments in more detail and uses the nm tool. Using objdump you can get a more verbose view.

--- uninited.c  2010-06-17 09:14:59.364090777 +0200
+++ initone.c 2010-06-17 09:15:57.144090752 +0200
@@ -1,5 +1,5 @@
 int my_int;
-int my_int2;
+int my_int2 = 3;
 int main() {
     return my_int + my_int2;
 }

Calling objdump on the compiled C programs shows you how my_int2 moves from the .bss section to the .data section between the two binaries.

$ objdump -zD uninited | sed -ne '/<main>/,/^$/p;/of section \.data/,/of section \.comment/p' 
...
Disassembly of section .bss:
...
000000000060102c <my_int2>:
  60102c: 00 00                 add    %al,(%rax)
  60102e: 00 00                 add    %al,(%rax)
...
$ objdump -zD initone | sed -ne '/<main>/,/^$/p;/of section \.data/,/of section \.comment/p'
...
Disassembly of section .data:
...
0000000000601018 <my_int2>:
  601018: 03 00                 add    (%rax),%eax
  60101a: 00 00                 add    %al,(%rax)
...

The moral of the story: globals in C are always initialized.

And, for the record, when using gcc — even without optimizations — there is no difference between initializing to 0 and leaving the variable uninitialized: they both go in the .bss. This means that you get no benefit from leaving a variable uninitialized. For readability, it is often better to explicitly initialize it anyway.

diff shell