Hi all, lately, after long days at work, I’ve been unwinding by playing games! It’s been my way of relaxing, but it’s also kept me from writing as much as I’d like. I guess we all need a break sometimes, right? 😀

Recently, I was working on a C++ project, it’s written by somebody, and I need to review it to make them buildable again on a new machine with the new environment and new challenges ahahah 🙂

While I was trying to find the required compilers, configurations, etc. I recognized that they have also a build order among their repositories. They have repositories providing DLLs, LIBs, and EXEs. Then it triggered me to wonder why I had not used them so far for my hobby projects, even just to get used to them. Here why we are here today 😀

First, let’s talk about DLLs and LIBs…

Choosing between a dynamic library (DLL) and a static library depends on several factors, including your project requirements, application performance, distribution needs, and development practices. Both have their pros and cons, and understanding when to use each can help optimize your application.


When to Use a Dynamic Library (DLL):

  1. Shared Code Across Multiple Applications:
    • If multiple applications or different parts of your project need to use the same code (e.g., utility functions, common libraries), a DLL is ideal.
    • The DLL can be shared across applications, which reduces memory usage and disk space, as only one copy of the library is loaded into memory or stored on disk.
  2. Modular or Plugin-Based Applications:
    • Applications designed to support plugins or extensions (e.g., browsers, video editors) often use DLLs to load additional functionality at runtime without recompiling the entire application.
    • You can update, replace, or add functionality by swapping DLLs without rebuilding the main application.
  3. Frequent Updates to Libraries:
    • If your library is updated frequently and you want to distribute updates without rebuilding and redistributing the entire application, DLLs make this easier. Only the DLL needs to be replaced, not the main EXE or other components.
  4. Reduced EXE Size:
    • Using DLLs can reduce the size of the EXE, as the application does not contain the code for the functions. Instead, the application will dynamically load the DLL at runtime.
  5. Memory Efficiency:
    • Since a DLL is loaded once into memory and can be shared by multiple running applications, using DLLs helps conserve memory, especially for large libraries or shared components.
  6. Delayed or Conditional Loading:
    • In some cases, you might want to load a library only if needed (e.g., for optional features). DLLs allow you to delay loading until runtime using LoadLibrary in Windows, which gives more control over when to load the code.

Drawbacks of DLLs:

  • Dependency Management: You need to ensure that the correct DLL version is available at runtime, leading to the infamous “DLL Hell” if different versions of the DLL conflict or aren’t properly managed.
  • Runtime Overhead: Since DLLs are loaded at runtime, there is a small overhead compared to static libraries, especially if the library is large or contains complex dependencies.

When to Use a Static Library:

  1. Self-Contained Executables:
    • If you want your application to be self-contained, with no external dependencies, use static libraries. All the code is bundled into the final EXE, and you don’t need to worry about distributing additional files like DLLs.
  2. Performance and Load Time:
    • Statically linked executables can be faster to load, as there’s no need to resolve dynamic symbols or load external libraries at runtime. This is particularly useful for time-sensitive or performance-critical applications, such as embedded systems or real-time applications.
  3. Simple Deployment:
    • Since everything is packaged into a single executable, deploying a static-linked application is simpler—no need to distribute additional DLLs or ensure they are installed correctly on the target system.
  4. Avoiding DLL Hell:
    • By statically linking libraries, you eliminate the possibility of “DLL Hell,” where different versions of a DLL may conflict or cause issues on a system.
  5. Security:
    • For applications that require a high level of security or are distributed in environments where updates are rare (e.g., firmware), static linking can prevent users from swapping out libraries to inject malicious code. Since the code is built directly into the executable, it’s harder to tamper with.

Drawbacks of Static Libraries:

  • Increased EXE Size: Since the static library code is embedded directly in the executable, it increases the size of the EXE. For larger libraries, this can make the application significantly bigger.
  • Memory Usage: Each application that statically links a library has its own copy of the code, leading to higher memory usage if multiple applications use the same library.
  • Rebuild Required for Updates: If the static library is updated, the entire application must be recompiled and redistributed, making it harder to push frequent updates compared to DLLs.

Key Considerations

Use DLLs WhenUse Static Libraries When
Code needs to be shared across multiple appsYou want self-contained executables
You need frequent updates or modular designYou want fast load times and simple deployment
Reducing EXE size is importantAvoiding external dependencies is critical
You want to load libraries at runtimeYou need better performance and security
You want efficient memory useApplication updates are infrequent or not needed

Example Use Cases:

  • DLL Example: Operating systems and large applications (e.g., browsers, games) use DLLs to share common libraries (like graphics engines or networking libraries) between different parts of the program or different programs.
  • Static Library Example: Embedded systems, firmware, or specialized applications with no external dependencies often use static libraries for simplicity and security.

I was also not aware of the differences between them in this much detail. I just got help from the search engines and, of course, AI chats 🙂

Here is my practical workshop about it: I decided to have a static and dynamic library and two identical main apps linked to each. So, I’m going to experience how they build, get linked, and also run

Main App + Static Library

MyMainApp.cpp
#include <iostream>
#include "MyStaticLib.h"

int main()
{
    int a, b, c, exit;

    std::cout << "Hello World!\n";

    std::cout << "Enter the first integer: ";
    std::cin >> a;

    std::cout << "Enter the second integer: ";
    std::cin >> b;

    std::cout << "Enter the third integer: ";
    std::cin >> c;
    
    int tmp_res = add(a, b);
    int res = add(tmp_res, c);

    std::cout << "Result = " << res << std::endl;

    std::cout << "\nPress any key and hit enter to end: ";
    std::cin >> exit;
}
MyStaticLib.cpp
#include "pch.h"
#include "MyStaticLib.h"

// TODO: This is an example of a library function
int add(int a, int b) {
    return a + b;
}
MyStaticLib.h
#pragma once

// Declare the function that will be defined in MyStaticLib.cpp
int add(int a, int b);
Building

I used VS2022 for my experiment.

Configuration PropertiesC++GeneralAdditional Include Directiories: <path>\repos\MyStaticLib\MyStaticLib

Configuration PropertiesLinkerGeneralAdditional Library Directiories: <path>\repos\MyStaticLib\MyStaticLib\<..(for me x64)..>\Debug

Configuration PropertiesLinkerInputAdditional Dependencies: MyStaticLib.lib

Build…

The result:

Main App + Dynamic Library

MyMainApp.cpp
#include <iostream>
#include "MyDynamicLib.h"

int main()
{
    int a, b, c, exit;

    std::cout << "Hello World!\n";

    std::cout << "Enter the first integer: ";
    std::cin >> a;

    std::cout << "Enter the second integer: ";
    std::cin >> b;

    std::cout << "Enter the third integer: ";
    std::cin >> c;
    
    int tmp_res = add(a, b);
    int res = add(tmp_res, c);

    std::cout << "Result = " << res << std::endl;

    std::cout << "\nPress any key and hit enter to end: ";
    std::cin >> exit;
}
MyDynamicLib.cpp
// MyDll.cpp
#include "pch.h"  // Precompiled header (if you have one)

// Export the function so it can be used by other projects
extern "C" __declspec(dllexport) int add(int a, int b) {
    return a + b;
}
MyDynamicLib.h
// MyDll.h
#pragma once

// Declare the add function
extern "C" __declspec(dllexport) int add(int a, int b);
Building

I used VS2022 for my experiment.

Configuration PropertiesC++GeneralAdditional Include Directiories: <path>\repos\MyDynamicLib\MyDynamicLib

Configuration PropertiesLinkerGeneralAdditional Library Directiories: <path>\repos\MyDynamicLib\MyDynamicLib\<..(for me x64)..>\Debug

Configuration PropertiesLinkerInputAdditional Dependencies: MyDynamicLib.lib

Build

Don’t forget to copy <path>\repos\MyDynamicLib\MyDynamicLib\<..(for me x64)..>\Debug\MyDynamicLib.dll into your debug directory.

The Result
Without DLL
With DLL.. no issue…

Output of the Experiment

As you can see above, the EXE linked to DLL is larger than the other one in terms of file size. This is not expected; the reason might be the library content since it’s too small and/or compiler optimization. The code initially was static, adding 4+3, I’ve changed it to the dynamic after I got this result, but still DLL linked EXE is more.

Why You Might Not See a Difference:

  1. Small Function Size: The add function is extremely small, and the size difference between linking statically or dynamically for such a basic function will be negligible. The actual machine code generated for such a simple function takes up very little space, so it won’t cause a large increase in the EXE size, whether statically linked or not.
  2. Optimization by the Compiler: Compilers often perform optimizations, such as inlining small functions like add. If the function is inlined, the code is directly inserted wherever the function is called, which could further reduce the difference between static and dynamic linking. When this happens, even when statically linking, the additional size in the EXE could be very small or even non-existent.
  3. Overhead in Executables: For such a small function, other components of the executable (like headers, metadata, and startup code) may take up more space than the actual function code itself, so the difference between linking statically or dynamically can be hard to notice.

When You Will See a Noticeable Size Difference:

You will likely see a clearer difference in EXE size when:

  • The DLL or static library contains multiple, larger functions or complex classes.
  • The static library is more substantial, containing larger pieces of code that would otherwise be linked directly into the EXE when using a static .lib.