Call mechanism of c ++ global constructor and destructor
The global variables of C ++ in the console EXE are initialized before Main and cleared after main. The VC compiler, linker, and VC Runtime library code work together to complete this magic. Copy this code to the console program you created, and create and run it:
# Include "stdafx. H"
# Include <iostream>
# Define secname ". CRT $ xcu"
# Pragma section (secname, long, read)
Void cleanup ()
{
STD: cout <"bye" <STD: Endl;
}
Void Init ()
{
// Do anything you want.
STD: cout <"hello" <STD: Endl;
Atexit (cleanup); // register the destructor
}
Typedef void (_ cdecl * _ pvfv )();
_ Declspec (allocate (secname) _ pvfv callb4main [] = {init };
Int main ()
{
STD: cout <"in main" <STD: Endl;
Return 0;
}
Output result:
Hello
In Main
Bye
Strange! You didn't call init and cleanup, but obviously they are called. What is the problem?
We know that the real entry function is maincrtstartup. For our discussion, maincrtstartup can be simplified as follows:
Int _ tmaincrtstartup () // this function will be linked to our EXE and used as the entry point function
{
_ Initterm ();
Int result = Main ();
Exit ();
Return result;
}
// File: CRT \ SRC \ crt0dat. c
Static void _ cdecl _ initterm (_ pvfv * pfbegin, _ pvfv * pfend)
{
While (pfbegin <pfend)
{
If (* pfbegin! = NULL)
(** Pfbegin )();
++ Pfbegin;
}
}
This is where the constructor is called. For every global variable we define, the compiler compiles its constructor call (no matter how many parameters you include) into a piece of code (the signature format is _ pvfv ), this Code also registers the Destructor with atexit (which is very easy for the compiler ). Then put the function pointer of this code into a _ pvfv array. The linker concatenates these _ pvfv arrays into a large array.
The pfbegin in the _ initterm parameter points to the first element of the array, and the pfend points to the last element of the array (always null), so our constructor is called, this is also the working principle of the above magic. The function registered by atexit will be executed after main exit (similar to the _ initterm code) and follow the LIFO principle. Therefore, the Destructor will be called in the reverse order of the constructor.
For non-console EXE and DLL, the situation is similar.
The real portal for any user-state process is kernel32.dll! _ Baseprocessstart @ 4, but this is irrelevant to our discussion.
Reference books:
Programmer self-cultivation-loading, linking and Library |