Linkage is the property that determines how identifiers (variables and functions) are connected across different parts of a program.
- Linkage determines whether an identifier can be shared across files or remains restricted to the file in which it is declared (or a single translation unit).
- It is different from scope, scope controls visibility, while linkage controls accessibility across files.
- A translation unit is a source file along with its included header files and dependencies, processed as a single unit by the compiler to generate object code.
- In a multi-file C program, each source file is compiled separately, and the linker combines multiple object files to produce the final executable.
- There are three types of linkage in C: Internal linkage, External linkage and No linkage.
Internal Linkage
An identifier implementing internal linkage is not accessible outside the translation unit it is declared in. Any identifier within the unit can access an identifier having internal linkage. It is implemented by the keyword static. An internally linked identifier is stored in initialized or uninitialized segment of RAM. For example,
Consider a source file: animals.c
#include <stdio.h>
// Variable with internal linkage
static int animals = 8;
The above code implements static linkage on identifier animals.
Consider another source file: feed.c is located in the same translation unit using #include
#include <stdio.h>
#include "animals.c"
int main() {
// Accessing variable.
printf("%d", animals);
return 0;
}
On compiling and executing feed.c using the following command:
gcc feed.c -o feed
./feed
We get the output,
8
Now, consider that feed.c is located in a different translation unit (means we are not including the animals.c using #include). Trying to compile it using the following command:
gcc feed.c animals.c -o feed
./feed
Compiler will throw an error
feed.c: In function 'main':
feed.c:6:18: error: 'animals' undeclared (first use in this function)
6 | printf("%d", animals);
| ^~~~~~~
feed.c:6:18: note: each undeclared identifier is reported only once for each function it appears in
External Linkage
An identifier implementing external linkage is visible to every translation unit. Externally linked identifiers are shared between translation units and are considered to be located at the outermost level of the program. It is the default linkage for globally scoped variables and functions.
The keyword extern implements external linkage. When we use the keyword extern, we tell the linker to look for the definition elsewhere. Thus, the declaration of an externally linked identifier does not take up any space. Extern identifiers are generally stored in initialized/uninitialized or text segment of RAM.
Take the above example of internal linkage and remove the static keyword.
animals.c
#include <stdio.h>
// Variable with external linkage
int animals = 8;
As the variable animals is declared globally, it is accessible to all the translation units.
Now, consider the file feed.c is in the different translational unit
#include <stdio.h>
// Telling compiler that the variable has
// external linkage
extern int animals;
int main() {
// Accessing variable.
printf("%d", animals);
return 0;
}
Compile and execute both files using the command:
gcc feed.c animals.c -o feed
./feed
Output:
8
The variable animals have external linkage that's why we can access it in the other translation unit.
Example of External and Internal Linkage
Below is an example of external and internal linkage. Modify the file animals.c as shown:
#include <stdio.h>
// Variable with internal linkage
static int animals = 8;
// Function with external linkage
void printAnimals() {
printf("%d\n", animals);
}
Here, animals variable have internal linkage (as it is declared static) while the function printAnimals() have external linkage (as it is declared globally).
Update the feed.c file as well
#include <stdio.h>
// Telling compiler that the function have
// external linkage
extern void printAnimals();
int main() {
printAnimals();
return 0;
}
The above code will print the value of "animals" variable because we are not accessing the value of animals in other translation unit. We are just accessing the function printAnimals() which is externally linked and in turn belongs to the same translation unit as animals. So it is able to access the value of the variable animals.