Sdílet prostřednictvím


Inicializace CRT

Tento článek popisuje, jak CRT inicializuje globální stav v nativním kódu.

Ve výchozím nastavení obsahuje linker knihovnu CRT, která poskytuje vlastní spouštěcí kód. Tento spouštěcí kód inicializuje knihovnu CRT, volá globální inicializátory a potom volá funkci poskytovanou main uživatelem pro konzolové aplikace.

Je možné, i když se nedoporučuje, využít chování linkeru specifického microsoftu k vložení vlastních globálních inicializátorů v určitém pořadí. Tento kód není přenosný a dodává se s některými důležitými upozorněními.

Inicializace globálního objektu

Vezměte v úvahu následující kód C++ (C tento kód nepovolí, protože nepovoluje volání funkce ve výrazu konstanty).

int func(void)
{
    return 3;
}

int gi = func();

int main()
{
    return gi;
}

Podle standardu func() C/C++ musí být volána před main() spuštěním. Ale kdo to říká?

Jedním ze způsobů, jak určit volající, je nastavit zarážku v func()aplikaci, ladit aplikaci a prozkoumat zásobník. Je možné, že zdrojový kód CRT je součástí sady Visual Studio.

Při procházení funkcí v zásobníku uvidíte, že CRT volá seznam ukazatelů na funkce. Tyto funkce jsou podobné func(), nebo konstruktory pro instance třídy.

CRT získá seznam ukazatelů na funkce z kompilátoru Microsoft C++. Když kompilátor uvidí globální inicializátor, vygeneruje dynamický inicializátor v oddílu .CRT$XCU , kde CRT je název oddílu a XCU je název skupiny. Pokud chcete získat seznam dynamických inicializátorů, spusťte příkaz dumpbin /all main.obja pak vyhledejte .CRT$XCU oddíl. Příkaz se použije pouze v případě, že main.cpp je zkompilován jako soubor C++, nikoli soubor C. Měl by se podobat tomuto příkladu:

SECTION HEADER #6
.CRT$XCU name
       0 physical address
       0 virtual address
       4 size of raw data
     1F2 file pointer to raw data (000001F2 to 000001F5)
     1F6 file pointer to relocation table
       0 file pointer to line numbers
       1 number of relocations
       0 number of line numbers
40300040 flags
         Initialized Data
         4 byte align
         Read Only

RAW DATA #6
  00000000: 00 00 00 00                                      ....

RELOCATIONS #6
                                               Symbol    Symbol
Offset    Type              Applied To         Index     Name
--------  ----------------  -----------------  --------  -------
00000000  DIR32             00000000           C         ??__Egi@@YAXXZ (void __cdecl `dynamic initializer for 'gi''(void))

CRT definuje dva ukazatele:

  • __xc_a v .CRT$XCA
  • __xc_z v .CRT$XCZ

Žádná skupina nemá žádné jiné symboly definované kromě __xc_a a __xc_z.

Když teď linker přečte různé .CRT pododdíly (část za $ním), zkombinuje je v jedné části a zařazuje je abecedně. To znamená, že uživatelem definované globální inicializátory (které kompilátor jazyka Microsoft C++ vkládá .CRT$XCU) vždy přicházejí za .CRT$XCA a před .CRT$XCZ.

Oddíl by měl vypadat podobně jako v tomto příkladu:

.CRT$XCA
            __xc_a
.CRT$XCU
            Pointer to Global Initializer 1
            Pointer to Global Initializer 2
.CRT$XCZ
            __xc_z

Knihovna CRT používá k __xc_z určení počátečního a koncového seznamu globálních inicializátorů z __xc_a důvodu způsobu, jakým jsou po načtení image rozloženy do paměti.

Funkce linkeru pro inicializaci

Standard C++ neposkytuje odpovídající způsob určení relativního pořadí napříč jednotkami překladu pro globální inicializátor zadaný uživatelem. Vzhledem k tomu, že linker Microsoft objednává .CRT pododdíly abecedně, je však možné využít toto pořadí k určení pořadí inicializace. Tuto techniku specifickou pro Microsoft nedoporučujeme a může dojít k přerušení v budoucí verzi. Zdokumentovali jsme ho jenom proto, abyste mohli vytvářet kód, který je poškozený těžko diagnostikovanými způsoby.

Abychom vám pomohli zabránit problémům v kódu, přidali jsme ve výchozím nastavení dvě nová upozornění: C5247 a C5248. Povolte tato upozornění a detekujte problémy při vytváření vlastních inicializátorů.

Inicializátory můžete přidat do nepoužívaných názvů vyhrazených oddílů a vytvořit je v určitém relativním pořadí, aby kompilátor vygeneroval dynamické inicializátory:

#pragma section(".CRT$XCT", read)
// 'i1' is guaranteed to be called before any compiler generated C++ dynamic initializer
__declspec(allocate(".CRT$XCT")) type i1 = f;

#pragma section(".CRT$XCV", read)
// 'i2' is guaranteed to be called after any compiler generated C++ dynamic initializer
__declspec(allocate(".CRT$XCV")) type i2 = f;

Názvy .CRT$XCT a .CRT$XCV nejsou používány kompilátorem nebo knihovnou CRT právě teď, ale neexistuje žádná záruka, že v budoucnu zůstanou nepoužité. A vaše proměnné by kompilátor stále mohl optimalizovat. Než začnete s touto technikou pracovat, zvažte potenciální problémy s inženýringem, údržbou a přenositelností.

Viz také

_initterm, _initterm_e
Soubory C runtime (CRT) a standardní knihovny C++ (STL) .lib