Nota
O acesso a esta página requer autorização. Podes tentar iniciar sessão ou mudar de diretório.
O acesso a esta página requer autorização. Podes tentar mudar de diretório.
Este artigo descreve como o CRT inicializa o estado global no código nativo.
Por padrão, o vinculador inclui a biblioteca CRT, que fornece seu próprio código de inicialização. Esse código de inicialização inicializa a biblioteca CRT, chama inicializadores globais e, em seguida, chama a função fornecida main pelo usuário para aplicativos de console.
É possível, embora não recomendado, aproveitar o comportamento do vinculador específico da Microsoft para inserir seus próprios inicializadores globais em uma ordem específica. Este código não é portátil e vem com algumas ressalvas importantes.
Inicializando um objeto global
Considere o seguinte código C++ (C não permitirá esse código porque não permite uma chamada de função em uma expressão constante).
int func(void)
{
return 3;
}
int gi = func();
int main()
{
return gi;
}
De acordo com o padrão C/C++, func() deve ser chamado antes main() de ser executado. Mas quem a chama?
Uma maneira de determinar o chamador é definir um ponto de interrupção no func(), depurar o aplicativo e examinar a pilha. É possível porque o código-fonte CRT está incluído no Visual Studio.
Ao navegar pelas funções na pilha, você verá que o CRT está chamando uma lista de ponteiros de função. Essas funções são semelhantes a func(), ou construtores para instâncias de classe.
O CRT obtém a lista de ponteiros de função do compilador Microsoft C++. Quando o compilador vê um inicializador global, ele gera um inicializador dinâmico na .CRT$XCU seção onde CRT é o nome da seção e XCU é o nome do grupo. Para obter uma lista de inicializadores dinâmicos, execute o comando dumpbin /all main.obje pesquise a .CRT$XCU seção. O comando só se aplica quando main.cpp é compilado como um arquivo C++, não um arquivo C. Deve ser semelhante a este exemplo:
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))
O CRT define dois ponteiros:
-
__xc_ano.CRT$XCA -
__xc_zno.CRT$XCZ
Nenhum dos grupos tem quaisquer outros símbolos definidos, exceto __xc_a e __xc_z.
Agora, quando o vinculador lê várias .CRT subseções (a parte após o $), ele as combina em uma seção e as ordena em ordem alfabética. Isso significa que os inicializadores globais definidos pelo usuário (que o compilador Microsoft C++ coloca em .CRT$XCU) sempre vêm depois de .CRT$XCA e antes de .CRT$XCZ.
A seção deve ser semelhante a este exemplo:
.CRT$XCA
__xc_a
.CRT$XCU
Pointer to Global Initializer 1
Pointer to Global Initializer 2
.CRT$XCZ
__xc_z
A biblioteca CRT usa ambos __xc_a e __xc_z para determinar o início e o fim da lista global de inicializadores devido à maneira como eles são dispostos na memória depois que a imagem é carregada.
Recursos do vinculador para inicialização
O padrão C++ não fornece uma maneira conforme de especificar a ordem relativa entre unidades de tradução para um inicializador global fornecido pelo usuário. No entanto, como o vinculador da Microsoft ordena as .CRT subseções em ordem alfabética, é possível aproveitar essa ordem para especificar a ordem de inicialização. Não recomendamos esta técnica específico da Microsoft, e poderá deixar de funcionar em uma versão futura. Nós o documentamos apenas para impedir que você crie código quebrado de maneiras difíceis de diagnosticar.
Para ajudar a evitar problemas em seu código, a partir da versão 16.11 do Visual Studio 2019, adicionamos dois novos avisos desativados por padrão: C5247 e C5248. Habilite esses avisos para detetar problemas ao criar seus próprios inicializadores.
Você pode adicionar inicializadores a nomes de seção reservados não utilizados para criá-los em uma ordem relativa específica aos inicializadores dinâmicos gerados pelo compilador:
#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;
Os nomes .CRT$XCT e .CRT$XCV não são usados pelo compilador ou pela biblioteca CRT no momento, mas não há garantia de que eles permanecerão sem uso no futuro. E as suas variáveis ainda podem ser otimizadas ou eliminadas pelo compilador. Considere os possíveis problemas de engenharia, manutenção e portabilidade antes de adotar essa técnica.
Ver também
_initterm, _initterm_e
Ficheiros de tempo de execução de C (CRT) e C++ Standard Library (STL) .lib