Static Library VS Shared Library

Introduction

Static libraries and shared libraries are two types of libraries commonly in C and C++ programming languages. They represent two different ways to package and distribute code.

In this blog post, we will discuss the differences between static libraries and shared libraries using an example approach.

Example

We will use an example to illustrate the difference between static libraries and shared libraries. All the source code is available on GitHub.

Implementation

In our example, we have two libraries, shape and rectangle, and one executable, main. The rectangle library depends on the shape library. The main executable depends on the rectangle library.

shape.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#ifndef SHAPE_H
#define SHAPE_H

#include <string>

class Shape
{
public:
Shape() = default;
Shape(std::string const& name);
virtual double area() const = 0;
std::string const& name() const;

protected:
std::string m_name;
};

#endif // SHAPE_H
rectangle.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#ifndef RECTANGLE_H
#define RECTANGLE_H

#include "shape.hpp"

class Rectangle : public Shape
{
public:
Rectangle(double w, double h);
Rectangle(double w, double h, std::string const& name);
double area() const override;

private:
double m_width;
double m_height;
};

#endif // RECTANGLE_H
main.cpp
1
2
3
4
5
6
7
8
9
10
11
#include "rectangle.hpp"
#include <iostream>
#include <string>

int main()
{
Rectangle rectangle{2.0, 3.0, std::string{"Rectangle"}};
std::cout << "Area of " << rectangle.name() << " is " << rectangle.area()
<< std::endl;
return 0;
}

Build Example

We will build the shape library as a static library shape_static and a shared library shape_shared.

CMakeLists.txt
1
2
3
4
5
6
7
8
9
10
11
cmake_minimum_required(VERSION 3.5.0)

project(Shape VERSION 0.0.1 LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# Create a shared library
add_library(shape_shared SHARED shape.cpp)
# Create a static library
add_library(shape_static STATIC shape.cpp)

We will build the rectangle library as a shared library that depends on the shape static library or the shape shared library, a static library that depends on the shape shared library or the shape static library.

CMakeLists.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
cmake_minimum_required(VERSION 3.5.0)

project(Rectangle VERSION 0.0.1 LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# Create a shared library that links to another shared library.
add_library(rectangle_shared_to_shape_shared SHARED rectangle.cpp)
target_include_directories(rectangle_shared_to_shape_shared PUBLIC ${CMAKE_SOURCE_DIR}/src/shape)
target_link_libraries(rectangle_shared_to_shape_shared PUBLIC shape_shared)

# Create a shared library that links to a static library.
# This does not work.
# add_library(rectangle_shared_to_shape_static SHARED rectangle.cpp)
# target_include_directories(rectangle_shared_to_shape_static PUBLIC ${CMAKE_SOURCE_DIR}/src/shape)
# target_link_libraries(rectangle_shared_to_shape_static PUBLIC shape_static)

# Create a static library that links to another shared library.
add_library(rectangle_static_to_shape_shared STATIC rectangle.cpp)
target_include_directories(rectangle_static_to_shape_shared PUBLIC ${CMAKE_SOURCE_DIR}/src/shape)
target_link_libraries(rectangle_static_to_shape_shared PUBLIC shape_shared)

# Create a static library that links to a static library.
add_library(rectangle_static_to_shape_static STATIC rectangle.cpp)
target_include_directories(rectangle_static_to_shape_static PUBLIC ${CMAKE_SOURCE_DIR}/src/shape)
target_link_libraries(rectangle_static_to_shape_static PUBLIC shape_static)

Shared libraries can only be linked to shared libraries, and static libraries can be linked to both shared libraries and static libraries.

Static library dependencies are resolved at compile time, and static library dependencies will become part of consumer executable or consumer static library.

The shared library dependencies are resolved at runtime, and shared library dependencies will not become part of consumer executable or consumer static library.

In our example, we have three rectangle libraries, rectangle_shared_to_shape_shared, rectangle_static_to_shape_shared, and rectangle_static_to_shape_static.

We will build the main executable in three different ways by linking to the three rectangle libraries.

CMakeLists.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
cmake_minimum_required(VERSION 3.5.0)

project(App VERSION 0.0.1 LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# Create an executable that links to a shared library that links to another shared library.
add_executable(app_to_rectangle_shared_to_shape_shared main.cpp)
target_include_directories(app_to_rectangle_shared_to_shape_shared PRIVATE ${CMAKE_SOURCE_DIR}/src/rectangle)
target_link_libraries(app_to_rectangle_shared_to_shape_shared PRIVATE rectangle_shared_to_shape_shared)

# Create an executable that links to a static library that links to another shared library.
add_executable(app_to_rectangle_static_to_shape_shared main.cpp)
target_include_directories(app_to_rectangle_static_to_shape_shared PRIVATE ${CMAKE_SOURCE_DIR}/src/rectangle)
target_link_libraries(app_to_rectangle_static_to_shape_shared PRIVATE rectangle_static_to_shape_shared)

# Create an executable that links to a static library that links to a static library.
add_executable(app_to_rectangle_static_to_shape_static main.cpp)
target_include_directories(app_to_rectangle_static_to_shape_static PRIVATE ${CMAKE_SOURCE_DIR}/src/rectangle)
target_link_libraries(app_to_rectangle_static_to_shape_static PRIVATE rectangle_static_to_shape_static)

The resulting main executables are app_to_rectangle_shared_to_shape_shared, app_to_rectangle_static_to_shape_shared, and app_to_rectangle_static_to_shape_static.

Run Example

After building the example, we can run the three main executables successfully.

1
2
3
4
5
6
$ ./build/src/app/app_to_rectangle_shared_to_shape_shared
Area of Rectangle is 6
$ ./build/src/app/app_to_rectangle_static_to_shape_shared
Area of Rectangle is 6
$ ./build/src/app/app_to_rectangle_static_to_shape_static
Area of Rectangle is 6

Inspect Example

We could inspect the dependencies of the three main executables using the ldd command. Because static libraries will become part of the consumer executable or consumer static library, only shared library dependencies will be shown.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
$ ldd build/src/app/app_to_rectangle_shared_to_shape_shared
linux-vdso.so.1 (0x00007fff854e2000)
librectangle_shared_to_shape_shared.so => /mnt/build/src/rectangle/librectangle_shared_to_shape_shared.so (0x0000706733636000)
libshape_shared.so => /mnt/build/src/shape/libshape_shared.so (0x0000706733631000)
libstdc++.so.6 => /lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007067333b1000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x0000706733384000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x0000706733172000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x0000706733087000)
/lib64/ld-linux-x86-64.so.2 (0x0000706733642000)
$ ldd build/src/app/app_to_rectangle_static_to_shape_shared
linux-vdso.so.1 (0x00007fff7676f000)
libshape_shared.so => /mnt/build/src/shape/libshape_shared.so (0x000075a693ab4000)
libstdc++.so.6 => /lib/x86_64-linux-gnu/libstdc++.so.6 (0x000075a693834000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x000075a693807000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x000075a6935f5000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x000075a69350c000)
/lib64/ld-linux-x86-64.so.2 (0x000075a693ac0000)
$ ldd build/src/app/app_to_rectangle_static_to_shape_static
linux-vdso.so.1 (0x00007ffd83ba6000)
libstdc++.so.6 => /lib/x86_64-linux-gnu/libstdc++.so.6 (0x000072eb4da73000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x000072eb4da46000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x000072eb4d834000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x000072eb4d74b000)
/lib64/ld-linux-x86-64.so.2 (0x000072eb4dcfa000)

The app_to_rectangle_shared_to_shape_shared executable depends on both the rectangle_shared_to_shape_shared shared library and the shape_shared shared library.

The app_to_rectangle_static_to_shape_shared executable depends on the shape_shared shared library.

The app_to_rectangle_static_to_shape_static executable depends neither of the rectangle libraries nor the shape libraries.

We could verify the dependencies by removing the shape shared library and the rectangle shared library.

1
2
3
4
5
6
7
8
9
10
11
12
$ rm -rf build/src/rectangle/
$ ./build/src/app/app_to_rectangle_shared_to_shape_shared
./build/src/app/app_to_rectangle_shared_to_shape_shared: error while loading shared libraries: librectangle_shared_to_shape_shared.so: cannot open shared object file: No such file or directory
$ ./build/src/app/app_to_rectangle_static_to_shape_shared
Area of Rectangle is 6
$ ./build/src/app/app_to_rectangle_static_to_shape_static
Area of Rectangle is 6
$ rm -rf build/src/shape/
$ ./build/src/app/app_to_rectangle_static_to_shape_shared
./build/src/app/app_to_rectangle_static_to_shape_shared: error while loading shared libraries: libshape_shared.so: cannot open shared object file: No such file or directory
$ ./build/src/app/app_to_rectangle_static_to_shape_static
Area of Rectangle is 6

Static Library VS Shared Library

From the example, we can see the pros and cons of static libraries and shared libraries.

Shared Library

Shared libraries make the consumer executable or consumer shared library more flexible and easier to update. Because shared libraries are not part of the consumer executable or consumer shared library, it’s possible to update the shared library without recompiling the consumer executable or consumer shared library. In addition, because shared libraries can be shared among multiple consumer executables or consumer shared libraries, using shared libraries can save disk space and memory.

Static Library

Static libraries makes the consumer executable or consumer static library more portable and easier to distribute. However, static libraries will increase the size of the consumer executable or consumer static library. In some scenarios, even if many consumer executables or consumer static libraries depend on the same static library, the static library will be duplicated in each consumer executable or consumer static library, which will waste disk space and memory.

References

Author

Lei Mao

Posted on

06-25-2024

Updated on

06-25-2024

Licensed under


Comments