There are two cases: at file scope (i.e. for a global declaration), and in a function.
In a function, the declaration int i;
does two things: it declares a variable called i
whose type is int
, and it reserves some storage in memory to put a value of type int
. What it does not do is give the variable a value. The storage used by i
will still contain whatever garbage was there before. You need to initialize the variable, i.e. assign a value to it, before you can read a value from it. Good compilers will warn you if you don't initialize the variable.
At file scope, int i
also declares a variable called i
. The rest depends on other things: this is known as a tentative definition. You can have multiple such declarations in your file. At most one of these is allowed to have an initializer, making it a full-fleged definition. If none of the declarations of i
at file scope have an initializer, the declaration is also a definition, and there is an implicit initialization to 0
. Thus:
int i;
/* ... more code ...*/
int i;
is valid, and i
will be initialized to 0 (assuming these are the only declarations of i
at file scope). Whereas:
int i;
int i = 3;
is also valid, and i
will be initialized to 3 when the program starts.
In practice, at file scope, there's often a difference between leaving the initialization implicit and explicitly initializing to 0. Many compilers will store an explicit 0 in the binary, but let the operating system initialize implicit zeroes automatically when the program is loaded. Don't worry about this unless you have a large global array (which shouldn't happen often) or you work on tiny embedded systems.