Anteckning
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
Den här artikeln beskriver hur CRT initierar det globala tillståndet i intern kod.
Som standard innehåller länkaren CRT-biblioteket, som tillhandahåller en egen startkod. Den här startkoden initierar CRT-biblioteket, anropar globala initierare och anropar sedan funktionen som tillhandahålls av main användaren för konsolprogram.
Det är möjligt, men rekommenderas inte, att dra nytta av Microsoft-specifika länkningsbeteenden för att infoga dina egna globala initierare i en viss ordning. Den här koden är inte portabel och innehåller några viktiga varningar.
Initiera ett globalt objekt
Överväg följande C++-kod (C tillåter inte den här koden eftersom den inte tillåter ett funktionsanrop i ett konstant uttryck).
int func(void)
{
return 3;
}
int gi = func();
int main()
{
return gi;
}
Enligt C/C++-standarden func() måste anropas innan main() körs. Men vem kallar det?
Ett sätt att fastställa anroparen är att ange en brytpunkt i func(), felsöka programmet och undersöka stacken. Det är möjligt eftersom CRT-källkoden ingår i Visual Studio.
När du bläddrar i funktionerna i stacken ser du att CRT anropar en lista med funktionspekare. Dessa funktioner liknar func(), eller konstruktorer för klassinstanser.
CRT hämtar listan över funktionspekare från Microsoft C++-kompilatorn. När kompilatorn ser en global initiering genererar den en dynamisk initiering i .CRT$XCU avsnittet där CRT är avsnittsnamnet och XCU är gruppnamnet. Om du vill hämta en lista över dynamiska initierare kör du kommandot dumpbin /all main.objoch söker sedan i .CRT$XCU avsnittet. Kommandot gäller endast när main.cpp kompileras som en C++-fil, inte en C-fil. Det bör likna det här exemplet:
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 definierar två pekare:
-
__xc_ai.CRT$XCA -
__xc_zi.CRT$XCZ
Ingen av grupperna har några andra symboler definierade förutom __xc_a och __xc_z.
Nu, när länkaren läser olika .CRT underavsnitt (delen efter $), kombinerar den de olika underavsnitten i ett avsnitt och ordnar dem alfabetiskt. Det innebär att de användardefinierade globala initierarna (som Microsoft C++-kompilatorn placerar i .CRT$XCU) alltid kommer efter .CRT$XCA och före .CRT$XCZ.
Avsnittet bör likna det här exemplet:
.CRT$XCA
__xc_a
.CRT$XCU
Pointer to Global Initializer 1
Pointer to Global Initializer 2
.CRT$XCZ
__xc_z
CRT-biblioteket använder både __xc_a och __xc_z för att fastställa början och slutet av den globala initieringslistan på grund av det sätt på vilket de läggs ut i minnet när avbildningen har lästs in.
Linker-funktioner för initiering
C++ Standard ger inget sätt att ange relativ ordning mellan översättningsenheter för en global initierare som tillhandahålls av användaren. Dock, eftersom Microsoft-länkaren ordnar underavsnitten .CRT alfabetiskt, kan man dra nytta av denna ordning för att specificera initialiseringsordning. Vi rekommenderar inte den här Microsoft-specifika tekniken och den kan brytas i en framtida version. Vi har dokumenterat det bara för att hindra dig från att skapa kod som är bruten på svårdiagnostiserade sätt.
För att förhindra problem i koden, från och med Visual Studio 2019 version 16.11, har vi lagt till två nya avstängda som standardvarningar: C5247 och C5248. Aktivera dessa varningar för att identifiera problem när du skapar dina egna initierare.
Du kan lägga till initialiserare i oanvända reserverade avsnittsnamn för att skapa dem i en specifik relativ ordning till kompilatorns genererade dynamiska initierare:
#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;
Namnen .CRT$XCT och .CRT$XCV används inte av kompilatorn eller CRT-biblioteket just nu, men det finns ingen garanti för att de förblir oanvända i framtiden. Och variablerna kan fortfarande optimeras bort av kompilatorn. Överväg potentiella problem med teknik, underhåll och portabilitet innan du använder den här tekniken.