In this tutorial, we’ll explain the differences between definition, declaration, and initialization in computer programming.
The distinction between the three concepts isn’t clear in all languages. It depends on the language we’re coding in and the thing we want to declare, define or initialize.
A declaration introduces a new identifier into a program’s namespace. The identifier can refer to a variable, a function, a type, a class, or any other construct the language at hand allows.
For a statement to be a declaration, it only needs to tell us what the declared identifier is. After reading a declaration statement, the code processor (compiler or interpreter) can differentiate between legal and illegal uses of the identifier.
For example, in C, we can declare a function by specifying its signature or a variable by specifying its type:
void g(int); int x;
We see that g is a function with an integer argument and no return value. Similarly, x is an integer variable. That’s everything a compiler needs to know about g and x to report incorrect use. For instance, x(g) would raise a compiler error, whereas g(x) wouldn’t.
Other languages work the same. Their syntax rules may differ, but none allows us to use an identifier before declaring it.
2.1. Formal Languages
There’s a fitting connection to the theory of formal languages. Let’s say our programming language’s syntax corresponds to a formal grammar with the set of production rules .
By declaring identifiers , we essentially derive a new grammar. The new set of symbols is plus the literals. The new rule set contains and the rules defining the declared identifiers’ syntactic categories.
This new grammar generates the set of all syntactically correct programs we can write using only , and literals.
2.2. What Declarations Can’t Do?
A declaration tells us what an identifier is. That isn’t enough.
Let’s go back to the above example. We can say that our C compiler wouldn’t complain about statements such as g(5). However, g doesn’t have a body specifying the operations it applies to its argument. As a result, we can’t execute any statement involving g, so we’ll get a runtime error if we try.
In other words, declaring an identifier doesn’t guarantee its existence during the runtime. It’s our job to make sure that’s the case by defining it.
When defining an identifier, we instruct the code processor to allocate, i.e., reserve a sufficiently large memory chunk for it. That means and requires different things depending on the identifier’s type.
For example, to define a function, we need to write its body. When handling the function’s definition, the processor converts it into machine code, places it in the reserved space, and links the function’s name to the place in memory containing the code. Without the body, the processor can’t know how much space to reserve.
To define a class in an object-oriented language, we implement its methods and specify its attributes. Similar goes for any type in any language.
To define a variable in a statically typed language, we need to specify its type explicitly. However, that’s the same as declaring it. So, a statement can both declare and define an identifier in some cases.
For instance, int x from the above example creates an identifier named x, reserves the space for an integer in the memory, and links the identifier to it:
Consequently, the linker knows where to find the value of x. But that doesn’t mean the variable will have any value when we try to use it. Further, how do we define variables in dynamically typed languages?
We solve those issues with initialization.
To initialize a variable, we assign it its starting value. That way, we make sure the expressions using the variable won’t throw a runtime error.
In a statically typed language, initialization and definition can be separate steps but can happen at the same time. For example:
int x; x = 5; int y = 5;
In dynamically typed languages, we define a variable by initializing it since there are no explicit type declarations:
x = 5
Further, depending on the language rules or the code processor, each type can have a default value. That means that every variable in such a statically typed language will get its type’s default value when we define it. If that’s the case, initialization is implicit.
In this article, we talked about declarations, definitions, and initialization. They mean different things but sometimes overlap in code.