Question
Static vs Shared Libraries in C/C++: What’s the Difference?
Question
What is the difference between static libraries and shared libraries in C/C++?
I am using Eclipse, and I see project types such as Static Library and Shared Library. What does each one mean, and does one have an advantage over the other?
Short Answer
By the end of this page, you will understand what static and shared libraries are, how they are linked into a program, and the practical trade-offs between them. You will also see when each type is commonly used in real C/C++ projects and how to choose the right one for your application.
Concept
A library is a collection of compiled code that other programs can use.
In C and C++, the two main kinds are:
- Static libraries
- Shared libraries (also called dynamic libraries)
The main difference is when the library code becomes part of your program.
Static library
A static library is linked into the executable at build time.
- On Linux, static libraries often use
.a - On Windows, they often use
.lib
When you build your program, the linker copies the needed parts of the static library into the final executable. After that, the executable contains its own copy of that code.
Shared library
A shared library is linked at runtime or prepared for runtime linking.
- On Linux, shared libraries often use
.so - On macOS, they often use
.dylib - On Windows, they are commonly
.dll
With a shared library, the executable usually contains a reference to the library instead of a full copy of its code. The operating system loads the shared library when the program starts, or sometimes later on demand.
Why this matters
This affects several important things:
- Executable size
- Memory usage
- Deployment
- Updates and bug fixes
- Compatibility
Core idea
- Static library = code is bundled into your program
- Shared library = code stays separate and is loaded when needed
Neither is always better. The right choice depends on your project goals.
Mental Model
Think of a library like a set of tools.
- With a static library, you pack the exact tools you need into your own toolbox before leaving home.
- With a shared library, you travel lighter and borrow the tools from a shared workshop when you arrive.
That leads to the trade-off:
- Packing your own tools means you are more independent.
- Borrowing shared tools means less duplication, but you must be sure the workshop has the right tools available.
In programming terms:
- Static means bigger self-contained executables
- Shared means smaller executables but an external dependency at runtime
Syntax and Examples
Basic idea
In C/C++, you usually:
- Write source files
- Compile them into object files
- Package object files into a library
- Link that library into an executable
Example library code
// math_utils.h
#ifndef MATH_UTILS_H
#define MATH_UTILS_H
int add(int a, int b);
#endif
// math_utils.cpp
#include "math_utils.h"
int add(int a, int b) {
return a + b;
}
Example program using the library
// main.cpp
#include <iostream>
#include "math_utils.h"
int main() {
std::cout << add(2, 3) << std::endl;
return 0;
}
Static library flow
The add function is compiled into a static library, and during linking, the needed code is copied into the final executable.
Result:
- The executable can often run without the
.aor.libfile being present later. - The executable is usually larger.
Shared library flow
The add function is compiled into a shared library, and the executable links to that external library.
Result:
- The executable is usually smaller.
- The shared library file must usually be available when the program runs.
Simplified build commands on Linux
Build a static library
g++ -c math_utils.cpp -o math_utils.o
ar rcs libmathutils.a math_utils.o
g++ main.cpp -L. -lmathutils -o app_static
Build a shared library
g++ -fPIC -c math_utils.cpp -o math_utils.o
g++ -shared -o libmathutils.so math_utils.o
g++ main.cpp -L. -lmathutils -o app_shared
Note
The exact commands vary by platform and build system, but the concept is the same:
- static: library code is copied into the executable
- shared: executable depends on an external library file
Step by Step Execution
Consider this code:
// main.cpp
#include <iostream>
#include "math_utils.h"
int main() {
int result = add(4, 5);
std::cout << result << std::endl;
return 0;
}
Suppose add is stored in a library.
If it is a static library
Step 1
The compiler compiles main.cpp into an object file.
Step 2
The linker sees that main calls add.
Step 3
The linker looks inside the static library and finds the compiled add function.
Step 4
The linker copies that machine code into the final executable.
Step 5
When the program runs, add is already inside the executable.
If it is a shared library
Step 1
The compiler compiles main.cpp into an object file.
Step 2
The linker records that add will come from a shared library.
Step 3
The final executable is created without embedding the full library code.
Step 4
When the program starts, the operating system loads the shared library.
Step 5
The call to add is resolved using the loaded shared library.
What changes in practice?
- Static: more is decided at build time
- Shared: more is resolved at load time or runtime
That is the key execution difference.
Real World Use Cases
When static libraries are useful
- Command-line tools distributed as a single file
- Embedded systems where deployment must be simple
- Internal utilities that should run without extra installed dependencies
- Builds that need maximum portability across machines with unknown library setups
Example:
- A small utility shipped to many servers where you do not want to install extra runtime libraries.
When shared libraries are useful
- Large applications split into reusable modules
- System libraries used by many programs
- Plugins and extension systems
- Applications that need updates without rebuilding everything
Example:
- Many programs on Linux use the same shared C standard library instead of each program containing its own private copy.
Why operating systems use shared libraries heavily
Shared libraries reduce duplication:
- less disk space overall
- less memory usage when many processes use the same library
- easier central updates for security fixes
Real Codebase Usage
In real projects, developers choose based on deployment, reuse, and maintenance needs.
Common static library patterns
- Bundling utility code used by one final application
- Vendoring third-party dependencies to simplify deployment
- Producing self-contained binaries for tools and installers
Example pattern:
- A project builds several internal static libraries such as
core,utils, andparsing, then links them into one executable.
Common shared library patterns
- Public SDKs distributed for other teams or customers
- Plugin architectures where modules are loaded separately
- Reusable platform components used by many executables
- System-level libraries maintained independently from applications
Practical concerns in real codebases
Versioning
Shared libraries require careful version compatibility. If a function signature changes, older executables may break.
Deployment
Static linking simplifies deployment because the executable is more self-contained. Shared linking requires shipping or installing the correct library versions.
Updates
If ten applications use one shared library, updating that library can fix a bug for all ten applications at once.
Build organization
A common structure is:
- static libraries for internal implementation pieces
- shared libraries for APIs intended to be reused by multiple programs
Error handling and startup issues
With shared libraries, programs can fail at startup if the required library file is missing or incompatible. Developers often need packaging, install scripts, or runtime path configuration to avoid this.
Common Mistakes
1. Thinking shared libraries are always better
They are not always better.
Shared libraries help with reuse and smaller executables, but they add runtime dependencies and versioning issues.
2. Thinking static libraries are always independent of everything
A statically linked library may still rely on other runtime components depending on how the program is built.
3. Forgetting runtime availability for shared libraries
A program may compile and link successfully, but fail to run if the shared library cannot be found.
Example problem:
./app_shared
# error while loading shared libraries: libmathutils.so: cannot open shared object file
Avoid this by:
- installing the library in a standard location
- setting library search paths correctly
- packaging the right runtime files
4. Ignoring binary compatibility
Changing a shared library API or ABI carelessly can break existing programs.
For example, changing class layouts or exported function signatures may cause runtime problems.
5. Mixing up build-time and runtime linking
Beginners often assume linking happens only once.
But with shared libraries:
- part of the decision happens at build time
- actual loading happens at runtime
6. Assuming smaller executable means smaller total deployment
A shared-library executable may be small, but you still need to ship the .so, .dll, or .dylib file.
Broken expectation example
// Program builds fine, but deployment is incomplete
// The executable depends on a shared library that is not shipped.
The code is not the problem. The packaging is.
Comparisons
Static vs shared libraries
| Feature | Static Library | Shared Library |
|---|---|---|
| Linked when | Build time | Load time / runtime |
| File types | .a, .lib | .so, .dll, .dylib |
| Executable size | Usually larger | Usually smaller |
| Runtime dependency | Usually less | Usually more |
| Updating library separately | No, usually rebuild needed | Yes, often possible |
| Memory sharing between processes | No separate shared copy | Often yes |
| Deployment simplicity | Often simpler | Often more complex |
| Version compatibility issues | Usually fewer at runtime | Usually more important |
Which should you choose?
| Situation | Better fit |
|---|---|
| Single self-contained tool | Static library |
| Many apps using the same code | Shared library |
| Easy deployment to unknown machines | Static library |
| Central security updates | Shared library |
| Plugin system | Shared library |
Shared libraries vs source code reuse
Sometimes instead of creating a reusable library, teams directly include source files in multiple projects. That is usually harder to maintain because:
- fixes must be copied everywhere
- builds become inconsistent
- reuse is less structured
Libraries provide a cleaner reuse boundary.
Cheat Sheet
Quick reference
- Static library: linked into the executable at build time
- Shared library: loaded separately at runtime
Common file extensions
| Platform | Static | Shared |
|---|---|---|
| Linux | .a | .so |
| Windows | .lib | .dll |
| macOS | .a | .dylib |
Rules of thumb
- Choose static when you want simpler deployment.
- Choose shared when multiple programs should reuse one library installation.
- Choose shared when updating the library independently matters.
- Choose static when runtime dependency problems would be painful.
Key trade-off
- Static = larger executable, fewer runtime dependencies
- Shared = smaller executable, more flexible updates, more deployment/version concerns
Important edge case
A small executable linked to a shared library is not fully standalone. It still needs the shared library at runtime.
Interview-style answer
A static library is copied into the executable during linking. A shared library stays separate and is loaded by the operating system when the program runs. Static linking simplifies deployment, while shared linking improves reuse and can reduce duplication across programs.
FAQ
What is the main difference between static and shared libraries?
A static library becomes part of the executable during build time. A shared library remains separate and is loaded when the program runs.
Are shared libraries faster than static libraries?
Not necessarily in a way that matters for most beginners. The main differences are deployment, memory sharing, and update flexibility, not usually obvious speed gains.
Why would I choose a static library?
Choose a static library when you want a more self-contained executable and easier deployment.
Why would I choose a shared library?
Choose a shared library when many programs use the same code or when you want to update the library without rebuilding every application.
Can a program fail because of a shared library?
Yes. If the required shared library is missing, in the wrong location, or incompatible, the program may fail to start.
Does Eclipse decide which one is better?
No. Eclipse only gives you project types and build options. The better choice depends on your project’s deployment and maintenance needs.
Are static libraries always larger?
Usually the final executable is larger because library code is copied into it. However, the exact size depends on how much code is actually linked.
Can I use both in the same project?
Yes. Real projects often use a mix of static and shared libraries depending on which components are internal, reusable, or distributed separately.
Mini Project
Description
Build a tiny C++ calculator library and use it from an application in two ways: once as a static library and once as a shared library. This demonstrates the exact difference in how the final executable depends on the library.
Goal
Create a reusable C++ library, link it into an app, and understand how static and shared builds affect the final program.
Requirements
[ "Create a library that exposes at least two functions such as add and multiply.", "Write a small application that calls those library functions.", "Build the library as a static library and link the application against it.", "Build the same library as a shared library and link the application against it.", "Observe the deployment difference between the two executables." ]
Keep learning
Related questions
2D Array Loop Order and Cache Performance in C
Learn why swapping nested loops changes 2D array performance in C, using row-major memory layout, cache locality, and practical benchmarks.
Array-to-Pointer Conversion in C and C++ Explained
Learn what array-to-pointer conversion means in C and C++, how array decay works, and how it differs from a pointer to an array.
Building More Fault-Tolerant Embedded C++ Applications for Radiation-Prone ARM Systems
Learn practical C++ and compile-time techniques to reduce soft-error damage in embedded ARM systems exposed to radiation.