Translation Units, Storage Duration, and Linker Symbol Resolution
Master the mechanics of internal vs external linkage, the One Definition Rule (ODR), symbol mangling, and debugging unresolved externals and duplicate symbols.
By the end, you’ll be able to…
- Distinguish between external linkage, internal linkage (static / anonymous namespaces), and no linkage
- Enforce the One Definition Rule (ODR) across multiple translation units without linker collisions
- Inspect mangled symbol names using nm and c++filt to resolve undefined reference errors
01The Linker's Role and Symbol Resolution
When developing non-trivial C++ systems, code is partitioned across multiple .cpp implementation files. Each .cpp file is compiled in complete isolation into an independent object file (.o). The compiler has no knowledge of functions or classes defined in other .cpp files; it trusts forward declarations and leaves symbol placeholder entries in the object file's relocation table.
The Linker (such as GNU ld, LLVM lld, or mold) is responsible for taking all separate object files and static libraries (.a / .lib), stitching their code and data sections together, and resolving all symbolic references into absolute memory addresses or PC-relative offsets.
If an identifier is referenced in fileA.cpp but never implemented anywhere in any linked object file, the linker halts with an 'undefined reference' error. Conversely, if a non-inline function is implemented in multiple object files with identical signatures, the linker halts with a 'multiple definition' error.
02Linkage Categories: External, Internal, and No Linkage
Linkage determines whether a name can refer to the same entity across different scopes and translation units. There are three fundamental linkage categories in C++: External Linkage, Internal Linkage, and No Linkage.
External Linkage: Names accessible across multiple translation units. Non-static global variables, non-static free functions, and classes have external linkage by default. The linker places these names in the global symbol table.
Internal Linkage: Names accessible ONLY within the translation unit where they are declared. Functions or global variables declared with the static keyword or placed inside an anonymous namespace (namespace { ... }) have internal linkage. The linker does not export these symbols, preventing naming collisions across modules.
03The One Definition Rule (ODR)
The One Definition Rule (ODR) is a cornerstone of the C++ standard. It establishes three fundamental guarantees: 1. Within any single translation unit, a variable, function, class, or template can have no more than one definition.
2. Within the entire program, non-inline functions and non-inline variables with external linkage must have EXACTLY ONE definition across all translation units.
3. Inline functions, inline variables (C++17), classes, and templates may be defined in multiple translation units (such as via header files), provided that each definition is completely identical in token sequence and semantic meaning.
Inspecting compiled symbols in an object file using 'nm':$ nm logger.o0000000000000000 T _ZN6Logger8log_infoEPKc U _ZSt4coutDemangling symbols using 'c++filt':$ c++filt _ZN6Logger8log_infoEPKcLogger::log_info(char const*)(T = Defined text/code section; U = Undefined reference awaiting linker resolution)04Symbol Mangling and Name Decoration
Because C++ supports function overloading, namespaces, and template instantiations, multiple distinct functions can share the exact same human-readable name (e.g., print(int) vs print(double)). The underlying binary object file format, however, requires every symbol to have a unique alphanumeric identifier.
The compiler solves this via Name Mangling (decoration): encoding the namespace hierarchy, class name, function name, and parameter types directly into the binary symbol name. When linking C libraries into C++, developers must wrap declarations in extern "C" { ... } to disable C++ name mangling and maintain ABI compatibility with C.
The ideas worth keeping.
- The Linker connects isolated object files by resolving symbols and calculating relocation addresses.
- Internal linkage (static / anonymous namespaces) prevents global symbol pollution across translation units.
- The One Definition Rule dictates exact uniqueness for non-inline entities and identical definitions for inline entities.
Make it yours.
Type and test your code right here in the interactive workspace. Run your solution, check the output, and compare with the worked guide when ready.
Exercise 1: Demonstrate how to declare a global configura
Demonstrate how to declare a global configuration variable in a header file using modern C++17 inline variables so it can be included in multiple .cpp files without ODR violations.
Explore a worked solution
// In config.hpp:#pragma once#include <string_view>// C++17 inline variable: exactly one shared instance across all translation units!inline constexpr std::string_view APP_VERSION = "2.4.0-production";inline constexpr int MAX_CONCURRENT_SOCKETS = 10000;Prefix the variable with inline constexpr in the header file.
Exercise 2: Explain the purpose of extern C when callin
Explain the purpose of extern "C" when calling a C library function from a C++ source file.
Explore a worked solution
// extern "C" instructs the C++ compiler to disable C++ name mangling for enclosed declarations.// This allows the linker to match the plain C symbol name in the compiled C library (.so/.lib)// rather than searching for a decorated C++ symbol encoding parameter types.Consider how C compilers generate symbol names compared to C++ compilers.
What stayed with you?
Check the ideas while they’re fresh. Your answers stay in this page and feedback appears as you go.
Exercise 1: Demonstrate how to declare a global configura
Demonstrate how to declare a global configuration variable in a header file using modern C++17 inline variables so it can be included in multiple .cpp files without ODR violations.
Build completed. Binary executed. [Exit code: 0]
Keep exploring.
The official references behind this lesson, when you want a second angle or the full details.
One lesson closer.
Reading is a start. Trying it yourself makes the connection.