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.obj
a 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