Dela via


CRT-initialisering

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_a i .CRT$XCA
  • __xc_z i .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.

Se även

_initterm, _initterm_e
.lib