Skip to content
VLSI Mentor

C++ Part I · Module 7 · Scope, Storage, and Namespaces

Internal Linkage: static at Namespace Scope and Anonymous Namespaces

This chapter pays a debt three chapters have been running up. Every example in Module 6 was marked static, and Chapter 6.1 could only ask you to read it as not visible outside this file and carry on. Chapter 7.3 then had to name the keyword as a genuine wart, because static on a local inside a function sets a storage duration while static at namespace scope does something completely unrelated — it does not change the duration at all, since the object already had it. What it changes is the third of the three questions this module separates: whether another translation unit can see the name. That property is called linkage, it is the one part of the module that is neither about names in source text nor about objects at run time, and it is the only one you can read straight out of a compiled object file. Two spellings produce it. One is inherited from C and still common; the other is the form C++ added, and the reason to prefer it is not a matter of taste — it is visible in what the compiler emits, where an anonymous namespace turns out to work by inventing a namespace name that no other file is able to write down.

Intermediate17 min readC++Part IFundamentalsScope

Part I · Module 7 · Chapter 7.5 · Internal Linkage: static at Namespace Scope and Anonymous Namespaces

1. The Third Question

Chapter 7.1 §1 listed three questions and took the first. Chapters 7.2 and 7.3 took the second. This is the third:

Linkage answers: can a name declared in this translation unit be referred to from another one?

Two answers matter here:

LinkageMeansHow you get it
externalother translation units can refer to this namethe default at namespace scope
internalonly this translation unit canstatic, or an anonymous namespace

Chapter 2.6 established the translation unit as the compiler's unit of work, and Chapter 6.1 §5 and Chapter 6.8 §3 both met the linker enforcing rules about names that span several. Linkage is the property that decides which names the linker even knows about.

Linkage is not scope. A name at namespace scope in a.cpp is not in scope in b.cpp either way — b.cpp would have to declare it to use it. What linkage decides is whether that declaration can succeed in finding a definition. Chapter 6.1's three-file arrangement is exactly this: a declaration in a header, a definition with external linkage, and a linker that connects them.

2. Where Linkage Applies

Only to names at namespace scope — and to class members, which is Module 11.

A local variable has no linkage at all. It cannot be referred to from another translation unit because it cannot be referred to from anywhere outside its block; that is Chapter 7.1's scope rule, and no keyword changes it.

This is the sharpest way to see Chapter 7.3 §5's wart:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
    void f() { static int n = 0; }   // changes DURATION; linkage is not involved
    static int n = 0;                 // changes LINKAGE; duration was already static

Same keyword, same spelling, two unrelated effects, decided entirely by where it is written. The second line's object already had static storage duration by virtue of being at namespace scope — Chapter 7.3 §2's question 3 — so static there has nothing left to do about duration and does something else instead.

3. Internal Linkage, Measured

The whole property is visible in the object file. Three files declaring the same two names three ways:

Azvya Education Pvt. Ltd.VLSI Mentor
ext.cpp — the default: external linkage
int helper(int n) { return n + 1; }          // external linkage
int shared_value = 7;                         // external linkage
Azvya Education Pvt. Ltd.VLSI Mentor
intern.cpp — static: internal linkage
static int helper(int n) { return n + 1; }   // internal linkage
static int shared_value = 7;                  // internal linkage
int use() { return helper(shared_value); }
Azvya Education Pvt. Ltd.VLSI Mentor
anon.cpp — an anonymous namespace: internal linkage, the C++ form
namespace {
    int helper(int n) { return n + 1; }      // internal linkage, C++ form
    int shared_value = 7;
}
int use() { return helper(shared_value); }

Compile each to an object file and ask nm what is in it:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$ nm ext.o
0000000000000000 T __Z6helperi
0000000000000018 D _shared_value

$ nm intern.o
000000000000001c t __ZL6helperi
0000000000000034 d __ZL12shared_value

$ nm anon.o
000000000000001c t __ZN12_GLOBAL__N_16helperEi
0000000000000034 d __ZN12_GLOBAL__N_112shared_valueE

Three things are in that output and all three matter.

The case of the letter is the linkage. T and D are a function and a data object the linker will expose to other object files; t and d are the same two things kept local to this one. That is the whole property, in one character — and it is the same column Chapter 6.8 §5 read to find weak external.

static adds an L to the mangled name. __Z6helperi became __ZL6helperi. Chapter 2.6 established that the compiler encodes a function's signature into its symbol name; the L is the encoding for "internal".

The anonymous namespace produces something else entirely — and §7 is about what.

4. What Internal Linkage Buys

Two translation units may each define a name with internal linkage, and the definitions are unrelated:

Azvya Education Pvt. Ltd.VLSI Mentor
s1.cpp
#include <iostream>
static int scale(int n) { return n * 2; }
int from_s1(int n) { std::cout << "  s1 scale -> " << scale(n) << '\n'; return scale(n); }
Azvya Education Pvt. Ltd.VLSI Mentor
s2.cpp
#include <iostream>
static int scale(int n) { return n * 10; }    // a DIFFERENT function, same name
int from_s2(int n) { std::cout << "  s2 scale -> " << scale(n) << '\n'; return scale(n); }
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$ c++ -std=c++17 -Wall -Wextra -Wpedantic s1.cpp s2.cpp smain.cpp -o sprog
$                                            ← zero diagnostics
$ ./sprog
  s1 scale -> 6
  s2 scale -> 30

No duplicate symbol, and each file called its own scale — 6 from n * 2 and 30 from n * 10 with n = 3.

Compare that directly with Chapter 6.8 §3, where two translation units each got a definition of twice and the link failed:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
duplicate symbol 'twice(int)' in:
    /private/tmp/odr/a.o
    /private/tmp/odr/b.o

Same situation, opposite outcome, and the difference is linkage. With external linkage the two definitions are two definitions of one entity, which the One-Definition Rule forbids. With internal linkage they are definitions of two different entities that happen to share a spelling, and nothing is violated.

So internal linkage buys two things:

A name you can choose freely. A helper called scale in one file does not constrain any other file.

A guarantee that nothing else depends on it. You can change or delete it knowing the only callers are in this file — which is a property no amount of discipline gives you for an external name.

5. When to Use It

Any helper function that exists only to serve this file. Every example in Module 6 was in this category, which is why they were all static.

Any file-local constant or table whose name would otherwise collide or invite use from elsewhere.

Not for anything a header declares. If a header declares it, other files are meant to call it. Measured, and better than you might expect:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
t.cpp:2:12: error: static declaration of 'scale' follows non-static declaration

A compile error, not a link error — because Chapter 6.1 §6's habit of including your own header means the compiler sees the header's int scale(int); and the file's static int scale(int) in one translation unit and rejects the contradiction on the spot. Omit that include and you would get Chapter 6.1 §5's Undefined symbols at link instead, with no line number.

Not as a substitute for inline in a header. That is Chapter 6.8's problem, and §9 explains why static is the wrong fix for it.

6. The Anonymous Namespace

C++ adds a second spelling:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
    namespace {
        int helper(int n) { return n + 1; }
        int shared_value = 7;
    }

A namespace with no name. Everything declared inside it has internal linkage, and — because there is no name to qualify with — you refer to its contents exactly as if they were declared at namespace scope directly.

§3 measured that it produces t and d, the same as static. The frozen purpose asks for more than that: why is this the C++ form?

7. Why It Is the C++ Form

Read the mangled names again:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
static            __ZL6helperi
anonymous namespace  __ZN12_GLOBAL__N_16helperEi

The second one contains _GLOBAL__N_1, and that is not a coincidence or a decoration. The compiler gave the unnamed namespace a real name — one it generated, and one that is unique to this translation unit.

That is the entire mechanism. The anonymous namespace is not a special linkage rule bolted on; it is an ordinary named namespace whose name you cannot write down. Nothing in another translation unit can refer to _GLOBAL__N_1::helper because nothing in another translation unit can name that namespace.

Three consequences follow, and they are why the C++ form is preferred:

It works for everything, not just functions and objects. static at namespace scope applies to functions and variables. An anonymous namespace can contain a type — a struct or class, Modules 9 and 11 — and give it internal linkage too. static cannot; the compiler says so:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
sty.cpp:1:1: warning: 'static' is not permitted on a declaration of a type

namespace { struct Entry { int id; }; } is how you make a type private to one file.

It groups. One namespace { } block holds every file-local declaration, so a reader sees the file's private section as a unit rather than scanning for a keyword repeated on twenty lines.

It cannot be forgotten halfway. Adding a declaration inside the block gets internal linkage automatically. Adding a declaration next to twenty static ones gets external linkage if you omit the keyword — and nothing warns.

And static is not deprecated. It is still perfectly correct for a function or a variable, it is shorter, and a great deal of C++ uses it. The honest position is that the anonymous namespace is the more general tool and the one to reach for when you have more than a couple of declarations or anything that is not a function or a variable.

8. The Practical Shape of a Source File

Putting Module 6 and this chapter together, a .cpp file has a natural structure:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
    #include "thing.h"        // my own header first -- Chapter 6.1 §6's check

    #include <iostream>       // then what I need

    namespace {               // my private section: nothing outside sees this
        int scale(int n) { return n * 2; }
        constexpr int kDefaultWidth = 8;
    }

    // then the definitions my header declared -- external linkage, on purpose
    int Thing::width() const { return kDefaultWidth; }

Three ideas from three chapters are doing work there. Including your own header first is Chapter 6.1 §6's habit, which turns a signature mismatch into a compile error. The anonymous namespace is this chapter. And the fact that everything outside it is externally visible by default is what makes the block worth having — the default is exposure, so privacy has to be asked for.

9. What to Actually Do

Give every file-local helper internal linkage. The default at namespace scope is exposure; privacy is opt-in.

Prefer an anonymous namespace for a file's private section, especially with more than one or two declarations, and always for a type.

static is fine for a single file-local function or variable, and is what a great deal of existing code uses.

Never use static on a function defined in a header. It links, and it gives every translation unit its own copy with its own statics — Chapter 6.8's inline is the answer.

Never give internal linkage to something a header declares. The link will fail, which is Chapter 6.1 §5's error on purpose.

Read nm when a linker error does not make sense. T/D against t/d answers "can anyone else see this?" in one character.

Put the anonymous namespace near the top, after the includes, so a reader meets the file's private section before its public one.

10. Mistakes Worth Making Once

Confusing the two meanings of static. On a local it sets duration; at namespace scope it sets linkage — Chapter 7.3 §5.

Thinking static at namespace scope changes the duration. The object already had static duration.

Using static on a header-defined function. Measured: it links silently, and the counter went 1, 2, 1, 2 across two files instead of 1, 2, 3, 4.

Marking something static that a header declares. Measured: error: static declaration of 'scale' follows non-static declaration — a compile error, because you included your own header.

Thinking the anonymous namespace is a special rule. Measured: it is a real namespace named _GLOBAL__N_1, invented per translation unit.

Using static on a type. It does not give a type internal linkage; an anonymous namespace does.

Adding a declaration next to twenty static ones and forgetting the keyword. It gets external linkage and nothing warns.

Assuming two files cannot both define scale. Measured: with internal linkage they can, and each called its own — 6 and 30.

11. Interview Relevance

"What does static mean in C++?" The complete answer is "it depends where you write it", and then both: on a local it gives static storage duration; at namespace scope it gives internal linkage; and on a class member it is a third thing (Chapter 11.8). Candidates who give one meaning have learned one context.

"What is an anonymous namespace for, and why prefer it to static?" Internal linkage, and the reasons are that it works for types as well as functions and objects, it groups a file's private section, and it cannot be half-forgotten. If you can add that the compiler implements it by generating a unique namespace name — visible in the mangled symbol — you are describing the mechanism rather than repeating the advice.

"What happens if you put a static function in a header?" It links, and every translation unit gets its own copy. That is the question that separates people who know the difference between static and inline from people who know that one of them fixes the error.

12. What You Should Be Able to Say Now

  1. Linkage answers the module's third question: can another translation unit refer to this name?
  2. External is the default at namespace scope; internal comes from static or an anonymous namespace.
  3. Linkage is not scope. A name in another file is not in scope there regardless; linkage decides whether a declaration can find a definition.
  4. It applies only at namespace scope (and to class members, Module 11). A local has no linkage.
  5. static means two unrelated things — duration on a local, linkage at namespace scope — and the second has nothing left to do about duration.
  6. Measured: nm shows T/D for external and t/d for internal. The case is the linkage.
  7. Measured: static inserts an L into the mangled name — __ZL6helperi.
  8. Measured: two translation units each defining static int scale(int) linked cleanly and each called its own — 6 and 30.
  9. That is the exact contrast with Chapter 6.8 §3's duplicate symbol 'twice(int)': same situation, opposite outcome, because of linkage.
  10. Internal linkage buys a freely chosen name and a guarantee nothing else depends on it.
  11. Measured: an anonymous namespace emits __ZN12_GLOBAL__N_16helperEi — the compiler invents a real, unique namespace name.
  12. That is why it works: no other translation unit can spell that namespace.
  13. Prefer it because it covers types too, it groups the private section, and it cannot be half-forgotten.
  14. Measured: static on a header-defined function links and duplicates — the counter gave 1, 2, 1, 2 at two addresses. inline is Chapter 6.8's answer, and §8's three problems have three different ones.

13. Exercises

Exercise 1 — name the linkage. For each, say external, internal, or none: (a) int f(int); at namespace scope (b) static int f(int); at namespace scope (c) int n; inside a function (d) static int n; inside a function (e) a function inside namespace { }.

Exercise 2 — read the symbol table. You run nm and see T __Z4readi, t __ZL5writei and d __ZN12_GLOBAL__N_15countE. For each, say whether another translation unit can refer to it and how it was declared.

Exercise 3 — two meanings. For each static, say whether it sets a duration or a linkage, and what the other property already was: (a) static int hits = 0; in a function (b) static int total = 0; at namespace scope (c) static void dump(); at namespace scope.

Exercise 4 — predict the link. Two translation units each define int scale(int). Say what happens, and what changes if both are marked static. Give the message for the failing case.

Exercise 5 — the silent mistake. A one-line function is defined in a header and marked static. Say why it links, what the program now contains, and what goes wrong if that function has a static local counter.

Exercise 6 — convert it. Rewrite this file's private section as an anonymous namespace, and say which of the four declarations could not have been made private with static:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
    static int scale(int n) { return n * 2; }
    static const int kWidth = 8;
    struct Entry { int id; int value; };
    static int lookup(int id);

Exercise 7 — three problems. For each, name the right tool from §8's callout and say why the other two are wrong: (a) a helper used only by thing.cpp (b) a one-line function used by five source files (c) a configuration object several files read during start-up.

14. Summary

  • Linkage decides whether another translation unit can refer to a name — the module's third question.
  • External by default at namespace scope; internal from static or an anonymous namespace.
  • Linkage is not scope, and a local has no linkage.
  • static means duration on a local and linkage at namespace scope — the wart Chapter 7.3 §5 named.
  • Measured: nm encodes linkage in the case of one letter — T/D against t/d.
  • Measured: static adds an L to the mangled name.
  • Measured: two files each defining static int scale(int) linked cleanly, each calling its own — 6 and 30 — against Chapter 6.8's duplicate symbol.
  • Measured: the anonymous namespace emits _GLOBAL__N_1 — an invented namespace name no other file can spell.
  • Prefer it for types, for grouping, and because it cannot be half-forgotten.
  • Measured: static in a header links and duplicates — 1, 2, 1, 2 at two addresses, with nm showing two copies of the function and its static local.

The anonymous namespace was used here for one property — that nobody else can name it — and that is a strange way to meet a feature whose entire purpose is naming. A namespace with a name is for the opposite problem: two libraries that both want to call something Buffer, two teams that both have a read, a vendor header that defines max. The next chapter takes namespaces as the subject rather than as a trick, including the one measurable surprise in them — a namespace is not a thing you define once, and the same block can be opened again on another line, in another file, by somebody who has never read yours.

Standards & specifications

Governing standard
ISO/IEC 14882 (the C++ language standard)(opens ISO/IEC (JTC 1/SC 22/WG 21) in a new tab)

Defines the C++ language and its standard library — the type system and templates, the object model and object lifetime, initialisation and overload resolution, the memory model and the containers, algorithms and utilities the library provides. Like C's standard it is a FAMILY of editions (C++98/03, C++11, C++14, C++17, C++20, C++23) and the editions are NOT interchangeable: a feature this curriculum relies on may be unavailable, differently specified or merely proposed in another edition, so each lesson names the edition its claim depends on. It does not define any foreign-function interface to a hardware description language, nor what a compiler actually emits, nor platform behaviour on real silicon — those are the implementation's and the device's contracts. Working drafts published by WG21 are freely available and are what most engineers read day to day; a draft is not the published standard.

This page also covers RTL structure, verification approach and debugging technique. Those are engineering practice built on the standard, not requirements the standard itself imposes.

Where this fits

Part of the C++ Programming curriculum.