C++ Linkage and Storage Duration: extern, static, thread_local

C++ Linkage and Storage Duration: extern, static, thread_local

이 글의 핵심

Linkage controls symbol visibility across translation units; storage duration controls object lifetime. Covers extern, static, thread_local, and initialization order pitfalls.

Linkage kinds

External linkage

Symbols visible to other translation units (e.g. non-inline int at namespace scope, non-static free functions).

// file1.cpp
int globalVar = 10;

void func() {
    std::cout << globalVar << std::endl;
}

// file2.cpp
extern int globalVar;
extern void func();

int main() {
    std::cout << globalVar << std::endl;
    func();
}

Internal linkage

static at file scope, or anonymous namespaces—not visible to other TUs.

static int internalVar = 10;

static void internalFunc() {
    std::cout << internalVar << std::endl;
}

No linkage

Local variables inside functions.


Storage duration

Automatic

Locals and parameters—destroyed at end of block.

Static

Globals, static locals, static class members—lifetime for entire program (or TU for internal linkage).

thread_local

Per-thread storage; each thread has its own instance.

Dynamic

new/delete (or smart pointers)—lifetime until explicitly freed.


extern usage

Variable declaration

// globals.cpp
int globalCounter = 0;

// main.cpp
extern int globalCounter;

const with extern

If you need shared const objects across TUs, use extern const consistently.

Explicit template instantiation (advanced)

Separate compilation patterns for templates may use extern template declarations—see your compiler docs.


static keyword

Function-local static

Initialized once, first time control passes through—thread-safe since C++11 for static locals.

Class static members

Declared in class; defined in one .cpp (or inline static in-class in C++17).


Common problems

Static initialization order fiasco

Avoid cross-TU initialization dependencies; prefer function-local statics for lazy init.

Duplicate definitions of globals

Only one definition across TUs for entities with external linkage.

thread_local cost

Each thread has its own copy—use for thread-local caches, RNG state, etc.


FAQ

Q1: extern vs static at file scope?

A: extern declares something defined elsewhere (often with external linkage). static at file scope gives internal linkage—not visible outside the TU.

Q2: When is thread_local useful?

A: Per-thread caches, per-thread PRNGs, TLS-style state.

Q3: Static initialization order?

A: Use function-local static for lazy initialization to avoid cross-TU ordering bugs.

Q4: Why extern “C”?

A: To link with C code by disabling C++ name mangling on those declarations.

Q5: Should I avoid globals?

A: Prefer narrower scope, dependency injection, or singletons where appropriate.

Q6: Further reading?

A: Stroustrup, cppreference, compiler manuals.


  • C++ Extern Linkage
  • C++ static members
  • C++ Dynamic Initialization

Practical tips

Debugging

  • Compiler warnings first.
  • Minimal repro.

Performance

  • Profile before optimizing.

Code review

  • Follow team conventions.

Practical checklist

Before coding

  • Right technique?
  • Maintainable?
  • Performance?

While coding

  • Warnings fixed?
  • Edge cases?
  • Error handling?

During review

  • Intent clear?
  • Tests?
  • Docs?

C++, linkage, storage, extern, static


  • C++ Extern Linkage
  • C++ CRTP
  • C++ Dynamic Initialization
  • C++ static initialization order
  • C++ Initialization Order