C ++ global variables

Source: Internet
Author: User

This is also a recently asked question. at which stage is the global variable initialized?

 

This problem has not been solved yet. Before the global variable is called by main after maincrtstartup, the application will complete the heap memory application (Remember where I can see that if entrypoint is changed, you need to apply for and manage the heap memory.).

The global variables are initialized at this stage.

 

Then I was asked where the global variable was released? The answer is that after the application exits, the main function exits, and the answer is correct.

But let's look back and think about it. How is the global variable initialized? I am not sure yet, so I am curious to study it carefully tonight!

First, write a SectionCodeAs follows:

 
// Header file class classsizeres {public: classsizeres (void );~ Classsizeres (void) ;}; // CPP file classsizeres: classsizeres (void) {} classsizeres ::~ Classsizeres (void) {}// the main function processes classsizeres staticobj; int _ tmain (INT argc, _ tchar * argv []) {//...}

The code is roughly like this, and then breakpoint debugging at the constructor. The call stack after interruption is found to be as follows:

Obviously, the initialization of global variables is indeed before the main call after maincrtstartup, and there is no difference with what we previously understood. But how does the compiler handle it?

Based on the call stack, we can find that the definition of function _ initterm is as follows:

# Ifdef crtdllvoid _ cdecl _ initterm (# else/* crtdll */static void _ cdecl _ initterm (# endif/* crtdll */_ pvfv * pfbegin, _ pvfv * pfend) {/** walk the table of function pointers from the bottom up, until * The end is encountered. do not skip the first entry. the initial * value of pfbegin points to the first valid entry. do not try to * execute what pfend points. only entries before pfend are Valid. */while (pfbegin <pfend) {/** if current table entry is non-null, call thru it. */If (* pfbegin! = NULL) (** pfbegin) (); // here is the key. This function is used to traverse the array of function pointers without parameters ++ pfbegin ;}}

The following function points to the address content as the key:

HerePfbegin =0x00f5b30c

View0x00f5b30c memory content

0x00f5b30c 00f57f60 00f57fc0 00f58020 (followed by 00000000)

Obviously, this is an array containing three elements. The key is what the three elements point.

The decompiling of 00f57f60 address is as follows:

Classsizeres staticobj;

@ 0

00f57f60 push EBP

00f57f61 mov EBP, ESP

00f57f63 sub ESP, 0c0h

00f57f69 push EBX

00f57f6a push ESI

00f57f6b push EDI

00f57f6c Lea EDI, [ebp-0C0h]

00f57f72 mov ECx, 30 h

00f57f77 mov eax, 0 cccccccch

00f57f7c rep STOs dword ptr es: [EDI]

00f57f7e mov ECx, offset staticobj (0f5e1e4h)

00f57f83 call classsizeres: classsizeres (0f000090h )//Call the constructor @ 1

00f57f88 push offset 'dynamic atexit destructor for 'staticobj '(0f590a0h)

00f57f8d call @ ILT + 190 (_ atexit) (0f510c3h)

00f57f92 add ESP, 4

00f57f95 pop EDI

00f57f96 pop ESI

00f57f97 pop EBX

00f57f98 add ESP, 0c0h

00f57f9e cmp ebp, ESP

00f57fa0 call @ ILT + 625 (_ rtc_checkesp) (0f51276h)

00f57fa5 mov ESP, EBP

00f57fa7 pop EBP

00f57fa8 RET

 

Classsizeres:

@ 2

00f000090 JMP classsizeres: classsizeres (0f516e0h) @ 3

 

Classsizeres Constructor

Classsizeres: classsizeres (void)

{

@ 3

00f516e0 push EBP

00f516e1 mov EBP, ESP

00f516e3 sub ESP, 0cch

00f516e9 push EBX

00f516ea push ESI

00f516eb push EDI

00f516ec push ECx

00f516ed Lea EDI, [ebp-0CCh]

00f516f3 mov ECx, 33 H

00f516f8 mov eax, 0 cccccccch

00f516fd rep STOs dword ptr es: [EDI]

00f516ff pop ECx

00f51700 mov dword ptr [ebp-8], ECx

}

00f51703 mov eax, dword ptr [this]

00f51706 pop EDI

00f51707 pop ESI

00f51708 pop EBX

00f51709 mov ESP, EBP

00f5170b pop EBP

00f5170c RET

 

The initialization process of global variables is as follows:

Step 1: After compilation, the compiler will generate some non-Argument functions based on the global variable declaration as above @ 0.

Step 2:ProgramAfter running, __tmaincrtstartup will call the _ initterm function to call the non-argument function generated by the compiler.

(** Pfbegin) (); // function pointer, pointing to the address where the compiler automatically generates a function without parameters @

Here, pfbegin-pfend all points to the global variable initialization function pfbegin generated by the compiler.

Step 3: the non-parameter global variable initialization function pfbegin will call the constructors of each class to complete object initialization @ 1

Step 3: @ 1 calls all types of constructor stubs (IAT stub addresses)

Step 4: @ 2 jump to the constructor to actually implement the address to complete object initialization

This step basically completes the initialization of a global variable.

How is the corresponding release implemented? Breakpoint under the Destructor! The call stack is found as follows:

Obviously, the corresponding destructor is called in doexit to complete the global variable structure.

Static void _ cdecl doexit (INT code, int quick, int retcaller) {# ifdef _ debug static int fexit = 0; # endif/* _ debug */# ifdef crtdll if (! Retcaller & check_managed_app () {/* only if the EXE is managed then we call corexitprocess. native cleanup is done in. cctor of the EXE if the EXE is native then native clean up shoshould be done before calling (CoR) exitprocess. */_ crtcorexitprocess (CODE) ;}# endif/* crtdll */_ lockexit (); /* assure only 1 thread in exit path */_ Try if (_ c_exit_done! = True) {_ c_termination_done = true;/* save callable exit flag (for use by Terminators) */_ exitflag = (char) retcaller;/* 0 = term ,! 0 = callable exit */If (! Quick) {/** do _ onexit/atexit () Terminators * (if there are any) ** these Terminators must be executed in reverse order (LIFO )! ** Note: * this Code assumes that _ onexitbegin points * to the first valid onexit () entry and that * _ onexitend points past the last valid entry. * If _ onexitbegin = _ onexitend, the table * is empty and there are no routines to call. */_ pvfv * onexitbegin = (_ pvfv *) decodepointer (_ onexitbegin); If (onexitbegin) {_ pvfv * onexitend = (_ pvfv *) decodepointer (_ onexitend); _ pvfv function_to_call = NULL;/* Save the start and end for later comparison */_ pvfv * begin = onexitbegin; _ pvfv * onexitend_saved = onexitend; while (1) {_ pvfv * onexitbegin_new = NULL; _ pvfv * onexitend_new = NULL;/* Find the last valid function pointer to call. */while (-- onexitend> = onexitbegin & * onexitend = _ encoded_null () {/* keep going backwards. */} If (onexitend <onexitbegin) {/* There are no more valid entries in the list, we are done. */break;}/* cache the function to call. */function_to_call = (_ pvfv) decodepointer (* onexitend );      ; // After decode, point to the resource release processing code generated by the compiler    /* Mark the function pointer as visited. */* onexitend = (_ pvfv) _ encoded_null ();/* call the function, which can eventually change _ onexitbegin and _ onexitend */(* function_to_call )(); // It is worth noting that a non-argument function calls the compiler to generate the resource release code, and then calls the destructor to complete the structure. It is similar to onexitbegin_new = (_ pvfv *) decodepointer (_ onexitbegin); onexitend_new = (_ pvfv *) decodepointer (_ onexitend); If (onexitbegin_saved! = Onexitbegin_new) | (onexitend_saved! = Onexitend_new) {/* reset only if either start or end has changed */onexitbegin = region = onexitbegin_new; onexitend = onexitend_saved = onexitend_new ;}}} # ifndef crtdll/** do pre-Terminators */_ initterm (_ xp_a, _ xp_z ); # endif/* crtdll */} # ifndef crtdll/** do Terminators */_ initterm (_ xt_a, _ xt_z ); # endif/* crtdll */# ifdef _ debug/* dump all memory leaks */If (! Fexit & _ crtsetdbgflag (_ crtdbg_report_flag) & _ flag) {fexit = 1; # ifndef crtdll _ freecrtmemory (); _ crtdumpmemoryleaks (); # endif/* crtdll */} # endif/* _ debug */}/* return to OS or to caller */_ finally if (retcaller) _ unlockexit (); /* unlock the exit code path */_ end_try_finally if (retcaller) return; _ c_exit_done = true; _ unlockexit (); /* unlock the exit code path */_ crtexitprocess (CODE );}

The detailed content is not repeated, similar to the structure, from

(* Function_to_call) ()-> code for processing resource generation failure by the compiler-> code for calling the Destructor stub function-> jump to the actual destructor address to release the execution Resource

The application for release of global variable resources has been basically completed.

 

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.