Reveals the structure and structure sequence of global variables in C ++

Source: Internet
Author: User

After completing the book "professional embedded software development-to fully develop high quality and efficient programming", I will focus my next book on the design and development of Visual Objects Based on C ++. From now on, I will launch blog posts on C ++ and high object design. The following is the topic.

We can see an interesting phenomenon about the initialization sequence of global variables in C ++ through the example program shown in Figure 1.

Class1.cpp

# Include <iostream>

Class class1_t

{

Public:

Class1_t ()

{

Std: cout <"class1_t: class1_t ()" <std: endl;

}

};

 

Static class1_t s_class1;

 

Main. cpp

# Include <iostream>

 

Class class2_t

{

Public:

Class2_t ()

{

Std: cout <"class2_t: class2_t ()" <std: endl;

}

};

 

Static class2_t s_class2;

 

Int main ()

{

Return 0;

}

 

Figure 1

The sample program defines a static global variable for a class and the class in two files respectively, and each type outputs its name in its constructor. For the sake of simplicity, the implementation of the main () function is empty. We know that global variables will be constructed before entering the main () function, and will be parsed only after exiting the main () function.

Figure 2 shows the running results of executable programs obtained by different compilation methods. The difference between the two compilation methods is that the order of main. cpp and class1.cpp in the compilation command is exchanged. From the results, the construction sequence of the two global variables in the sample program is related to the position of the file during compilation.

$ G ++ main. cpp class1.cpp-o example

$./Example.exe

Class1_t: class1_t ()

Class2_t: class2_t ()

$ G ++ class1.cpp main. cpp-o example

$./Example.exe

Class2_t: class2_t ()

Class1_t: class1_t ()

Figure 2

Why is there such an interesting phenomenon? We need to know how the compiler handles global variables. This requires you to view the source code of the compiler and use the binutils toolset.

It is certain that the file order during compilation will affect the processing sequence of the target file by the ld linker. Let's first understand the default link script of the ld linker. You can use the command in Figure 3 to obtain the link script that comes with ld. Figure 4 shows the script fragments that need to be concerned.

$ Ld -- verbose> ldscript

Figure 3

Ldscript

/* Script for ld -- enable-auto-import: Like the default script failed t

Read only data is placed into. data */

SECTIONS

{

/* Make the virtual address and file offset synced if

Alignment is lower than the target page size .*/

. = SIZEOF_HEADERS;

. = ALIGN (_ section_alignment __);

. Text _ image_base _ + (_ section_alignment _ <0x1000? .: _ Section_alignment __):

{

* (. Init)

* (. Text)

* (SORT (. text $ *))

* (. Text .*)

* (. Glue_7t)

* (. Glue_7)

___ CTOR_LIST _ =.; _ CTOR_LIST _ = .;

LONG (-1); * (. ctors); * (. ctor); * (SORT (. ctors. *); LONG (0 );

___ DTOR_LIST _ =.; _ DTOR_LIST _ = .;

LONG (-1); * (. dtors); * (. dtor); * (SORT (. dtors. *); LONG (0 );

* (. Fini)

/*??? Why is. gcc_exc here? */

* (. Gcc_exc)

PROVIDE (etext = .);

* (. Gcc_effect_table)

}

......

}

Figure 4

Pay attention to the 18 ~ 21 rows. These rows are used to construct global variables in all program files (including the target files and library files) and put the function pointers of The Destructor into the corresponding array. From the perspective of C ++, The __ctor_list _ array is used to store pointers of global class variable constructors, while the _ DTOR_LIST _ array is used to store destructor. Note that for Constructor data, it is composed of. ctors,. ctor, and program segments containing. ctors. In each program file. In addition, the first item of the two data must be-1, and the last item must be 0.

By viewing the gcc source code (the implementation of g ++ is also in it), you can see the Declaration of two arrays from the gbl-ctors.h, learn how global class variable constructor and destructor are called from the libgcc2.c file, as shown in Figure 5. Note that the sample code is removed for simplified purposes.

Gbl-ctors.h

Typedef void (* func_ptr) (void );

 

Extern func_ptr _ CTOR_LIST _ [];

Extern func_ptr _ DTOR_LIST _ [];

 

# Define DO_GLOBAL_CTORS_BODY \

Do {\

Unsigned long nptrs = (unsigned long) _ CTOR_LIST _ [0]; \

Unsigned I ;\

If (nptrs = (unsigned long)-1 )\

For (nptrs = 0; _ CTOR_LIST _ [nptrs + 1]! = 0; nptrs ++ );\

For (I = nptrs; I> = 1; I --)\

_ CTOR_LIST _ [I] (); \

} While (0)

Libgcc2.c

Void _ do_global_dtors (void)

{

Static func_ptr * p = _ DTOR_LIST _ + 1;

While (* p ){

P ++;

(* (P-1 ))();

}

}

 

Void _ do_global_ctors (void)

{

DO_GLOBAL_CTORS_BODY;

Atexit (_ do_global_dtors );

}

Figure 5

You can see from the two files in the figure that the constructor of global variables is called through the _ do_global_ctors () function. According to the implementation of the DO_GLOBAL_CTORS_BODY macro, the number of constructors in the array is obtained in rows 11 and 12, and each constructor is called in reverse order in rows 13 and 14. The _ do_global_ctors () function finally calls the atexit () function of library C to register the _ do_gloabl_dtors () function, so that the function can be called when the program exits.

According to the implementation of the _ do_global_dtors () function, the destructor of each global variable are called sequentially, which is the opposite of the order of calling the constructor. This ensures that "the global class variables constructed first are parsed ."

The call to the _ do_gloable_ctors () and _ do_gloable_dtors () functions is called by building code in the C ++ language environment. In general, they are called respectively when entering and exiting the main () function.

We can use the objdump in the binutils tool to verify the content described above. Figure 6 demonstrates the disassembly code of the class1.o target file. Readers do not need to read the assembly code carefully, but pay attention to the two functions in the position of 4a and 66. The former is the destructor of the s_class1 variable in the class1.cpp file, and the latter is the corresponding constructor.

$ G ++-c-g class1.cpp

$ Objdump-S-d -- demangle = gnu-v3 class1.o

 

Class1.o: file format pe-i386

 

 

Disassembly of section. text:

 

...... Content deleted ......

10000004a <global destructors keyed to class1.cpp>:

4a: 55 push % ebp

4b: 89 e5 mov % esp, % ebp

4d: 83 ec 08 sub $0x8, % esp

50: c7 44 24 04 ff 00 movl $0 xffff, 0x4 (% esp)

57: 00

58: c7 04 24 00 00 00 00 movl $0x0, (% esp)

5f: e8 9c ff call 0

64: c9 leave

65: c3 ret

 

00000066 <global constructors keyed to class1.cpp>:

66: 55 push % ebp

67: 89 e5 mov % esp, % ebp

69: 83 ec 08 sub $0x8, % esp

6c: c7 44 24 04 ff 00 movl $0 xffff, 0x4 (% esp)

73: 00

74: c7 04 24 01 00 00 00 movl $0x1, (% esp)

7b: e8 80 ff call 0

80: c9 leave

81: c3 ret

82: 90 nop

83: 90 nop

Figure 6

Figure 7 shows how to use the objdump tool to view the content in the. ctors and. dtors sections of the class1.o file. From the content, we can see that there are two values 4a and 66 mentioned above, and these two values will be put into the _ CTOR_LIST _ and _ DTOR_LIST _ arrays respectively by the ld linker.

$ Objdump-s-j. ctors class1.o

 

Class1.o: file format pe-i386

 

Contents of section. ctors:

0000 66000000 f...

$ Objdump-s-j. dtors class1.o

 

Class1.o: file format pe-i386

 

Contents of section. dtors:

0000 4a000000 J...

Figure 7

After learning about how the compiler processes the constructor and destructor of global class objects, it is not difficult to understand the interesting phenomena mentioned at the beginning. This is because the location order during file compilation will eventually affect the construction of global variables and the order of destructor in the _ CTOR_LIST _ and _ DTOR_LIST _ arrays.

What is the significance of understanding this content? This helps us understand how to correctly implement the singleton Design Pattern in C ++. This topic will be discussed in another blog.

 

This article is from the "Li Yun" blog

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.