Initializers in C
Initializers in C language allow assigning values to variables from their declaration, improving code clarity and reducing potential errors.
In this lesson we will explore the different types of initializers, the rules that govern their validity and the differences between variables with automatic and static storage duration.
Initializers
For convenience, C allows us to specify initial values for variables while we declare them.
We have, in fact, seen in previous lessons that to initialize a variable, we write the = symbol after its declarator, followed by an initializer. (Let's not confuse the = symbol in a declaration with the assignment operator: initialization is not the same thing as assignment.)
We have already seen various types of initializers in previous lessons. The initializer of a simple variable is an expression of the same type as the variable:
int i = 5 / 2; /* i is initially 2 */
If the types do not match, C converts the initializer using the same rules as assignment:
int j = 5.5; /* converted to 5 */
The initializer of a pointer variable must be a pointer expression of the same type as the variable or of type void *:
int *p = &i;
The initializer for an array, structure, or union is usually a series of values enclosed in braces:
int a[5] = {1, 2, 3, 4, 5};
Furthermore, we have seen that in C99 we can use the so-called designated initializers:
int a[5] = {[2] = 3, [4] = 5};
/* is equivalent to */
int a[5] = {0, 0, 3, 0, 5};
To complete the overview on declarations, let's now examine some additional rules that govern initializers:
-
An initializer for a variable with static storage duration must be constant:
#define FIRST 1 #define LAST 100 static int i = LAST - FIRST + 1;Since
LASTandFIRSTare macros, the compiler can calculate the initial value (100 - 1 + 1 = 100). IfLASTandFIRSThad been variables, the initializer would have been illegal. -
If a variable has automatic storage duration, its initializer does not necessarily have to be constant:
int f(int n) { int last = n - 1; ... } -
An initializer enclosed in braces for an array, structure, or union must contain only constant expressions, never variables or function calls:
#define N 2 int powers[5] = {1, N, N*N, N*N*N, N*N*N*N};Since
Nis a constant, the initializer forpowersis valid; ifNwere a variable, the program would not compile. In C99, this restriction applies only if the variable has static storage duration. -
The initializer for an automatic structure or union can be another structure or union:
void g(struct part part1) { struct part part2 = part1; ... }The initializer does not necessarily have to be the name of a variable or parameter, but must be an expression of the correct type. For example, the initializer of
part2could be*p, wherepis of typestruct part *, orf(part1), wherefis a function that returns a structure of typepart.
Uninitialized Variables
In previous lessons we have seen that uninitialized variables have undefined values. This is because they might have the value of a previously occupied memory area, or they might be allocated in a part of memory that has not been initialized. However, it is a common mistake to think that uninitialized variables are always set to zero.
This is not always true though: the initial value of a variable depends on its storage duration.
-
Variables with automatic storage duration do not have a default initial value. The initial value of an automatic variable cannot be predicted and can vary each time the variable comes into existence.
-
Variables with static storage duration assume the value zero by default. Unlike memory allocated with
calloc, which simply sets the bits to zero, a static variable is correctly initialized according to its type: integer type variables are initialized to0, floating-point ones to0.0, while pointers contain a null pointer.
From a stylistic point of view, it is preferable to provide initializers for static variables rather than relying on the fact that they are guaranteed to be zero. If a program accesses a variable that has not been explicitly initialized, the reader of the program cannot easily determine whether the variable is actually considered zero or whether it is initialized at another point in the program.
In Summary
In this lesson we have seen that:
- An initializer is specified by writing
=after the declarator, followed by an expression or a list of values (in the case of arrays, structures and unions). - Variables with static storage duration must have a constant initializer, while automatic ones can be initialized with dynamic values.
- In C99 it is possible to use designated initializers and other advanced forms to initialize arrays and structures in a more flexible way.
- Uninitialized variables have different behaviors depending on their storage duration: static ones are zeroed, while automatic ones have an indeterminate value.
- It is good practice to always provide initializers, especially for static variables, to avoid ambiguity and make the program's behavior clear.