TGViewer
Грокаем C++ Грокаем C++ @grokaemcpp · 9.35K subscribers
Post #276 3.53K
Вот когда точно статики инициализируются после main

Все-таки есть стопроцентный способ создать условия, чтобы этот эффект проявился.

Как вы знаете, есть 2 вида библиотек: статические и динамические. Код статических библиотек вставляется в основной код программы в то время, как код динамических библиотек подгружается в рантайме.

Так вот есть способы в любой момент исполнения программы руками подгрузить shared library и использовать ее символы, даже ничего не зная о ней на этапе линковки объектников!

На юниксах это системный вызов dlopen. Он принимает путь к библиотеки и возвращает ее хэндл. Через этот хэндл можно получать указатели на сущности из либы.

Естественно, что раз бинарник ничего не знал о сущностях библиотеки до ее explicit подгрузки, а библиотека просто лежала камнем в файловой системе, то буквально никакой код библиотеки не может быть выполнен до ее подгрузки. А значит, если мы открываем либу в main(), то только в этот момент начинается вся динамическая инициализация сущностей либы. Поэтому значение ее переменных со static storage duration устанавливается после входа в main()!

Минимальный пример:

// lib.cpp
struct CreationMomentShower {
CreationMomentShower(int num=0) : data{num} {
std::cout << "Created object with data " << num << std::endl;
}
int data;
};

struct Use {
static inline CreationMomentShower help{6};
};

// main.cpp
#include <iostream>
#include <dlfcn.h>

int main()
{
std::cout << "Main has already started" << std::endl;
void* libraryHandle = dlopen("libsource.so", RTLD_NOW);
if (libraryHandle == nullptr) {
std::cerr << dlerror() << std::endl;
return 1;
}
dlclose(libraryHandle);
}


Вывод:

Main has already started
Created object with data 6


Чтобы запустить это дело(на примере gcc), нужно:

1️⃣ Скомпилировать объектный файл из source.cpp g++ -c -fpic -std=c++17 source.cpp

2️⃣ Превратить его в библиотеку g++ -shared -o libsource.so source.o

3️⃣ Скомпилировать main.cpp g++ -o test main.cpp -std=c++17

4️⃣ Запустить ./test

Важно отметить, что о существовании библиотеки исполняемый файл test вообще не в курсе. Также не нужно добавлять путь до либы в какой-нибудь $LD_LIBRARY_PATH.

Если библиотеку сликовать с бинарем сразу же и подгружать ее неявно, то порядок инициализации будет снова неопределен в соотвествии с предыдущим постом. И скорее всего такого эффекта в этом случае не будет.

Вот такие интересности существуют в мире инициализации статиков. Пост не несет какого-то особого смысла, разве что познакомить некоторых читателей с таким вот явным способом загрузки динамических либ в код.

Dig deeper. Stay cool.

#compiler
  • 👍 18
  • 🔥 11
  • 🤔 3
  • ❤ 1
More from @grokaemcpp
  1. Oct 14, 2026​​Что внутри std::condition_variable? #опытным Более менее все знакомы с примитивом синхро…
  2. Oct 12, 2026​​WAT #новичкам Как вы думаете, результат выражения: bool math = -1 < 1U; будет правдой ил…
  3. Oct 8, 2026​​Strict weak ordering #опытным На первый взгляд, всё выглядит рабочим: мы создаём 40 зака…
  4. Oct 7, 2026​​Где-то баг... #опытным Вот вам код: struct Order { int price; int id; }; int main() { st…
  5. Oct 5, 2026Откуда spurious wakeup на кондваре? #опытным У кондваров есть метод std::condition_variabl…
  6. Oct 1, 2026​​Stacktrace. Tips #опытным Чтобы полноценно работать со стандартными трейсами, нужно знат…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →