VC ++ 6.0 Compiler Memory Allocation Model during compilation (memory layout)
---- Reposted from the network
I. Division of memory areas
The memory occupied by a C/C ++ compiled program is divided into the following parts:
1) Stack: it is automatically allocated and released by the Compiler (Compiler), and stores function parameter values and locally changed values. The operation method is similar to the stack in the data structure.
2) Heap: Generally, it is assigned and released by the programmer. If the programmer does not release the Heap, it may be recycled by the OS at the end of the program. Note that it is different from the heap in the data structure.
The method is similar to the linked list.
3) Global (static): stores global variables and static variables. initialized global variables and static variables are in the same region, but not initialized completely.
Local variables and uninitialized static variables are in another adjacent area. The program is released by the system.
4) text Constant Area: constant strings are placed here. The program is released by the system.
5) program code area: stores the binary code of the function body.
Ii. Test Cases (comparison between source code and disassembly)
2.1 comparison between source code and disassembly of Test Cases
To visually describe the memory layout model, let's take a look at the Win32 Console Application code (Table 3.1), where bold text (the leftmost end of the row is the row label)
Is the C source code, without bold text (the leftmost side of the line is the address) is the command code after disassembly. It looks messy, but it must be patient. The text behind it will be based on this.
3.2 memory Layout
In this case, the following figure shows the five memory areas mentioned in section 2nd. Note that the starting address of each region in the figure is not absolute, and the compiling environments may be different. Here is a typical example. Note that the address addressing directions vary in different regions.
3. Applications
Through understanding the case in section 3rd, we will explain some phenomena.
3.1. Variable Initialization
1) local variables will be allocated to the stack. If they are not initialized, they will be unpredictable values. During compilation, the compiler will throw a warning error (local variable' variable name 'used without having been initialized) numbered c4700 ).
Table 4.1 code tests whether local variables are not initialized.
A typical output result of this test is-858993460. At the same time, the compiler throws a warning error during compilation.
2) If the global variable is not initialized, the default value is 0. The Compiler does not prompt "the variable is not initialized" during compilation ".
Table 4.2 code tests whether the global variables are not initialized.
The output result of this test is: 0.
3) The initialization effect of global variables is the same as that of non-initialization. Please note that table 3.1 contains 9th lines of code, that is
Int init_array_g1 [10] = {0}; // initialized Global Array 1
It is equivalent:
Int init_array_g1 [10]; // initialized Global Array 1
Of course, we recommend that you initialize the global variables out of caution.
3.2 influence of variable Initialization on code space
This section discusses variable initialization, But we separate it into a section out of attention. Now let's look at two test cases.
Case 1: Create a Win32 Console Application project named Test1. The code is shown in table 4.3.
Compile the file into a debugexample and check the size of the test1.exe executable file in the debugdirectory. The typical size is about 0.18 KB (about MB ).
Case 2: Create a Win32 Console Application project named Test2. The code is shown in table 4.4.
Compile the file into debug.pdf and check the executable file test2.exe in the debugdirectory. The typical size is about 46 MB.
The only difference between the two cases is that the first element in the init_array_g1 [] array is initialized with 0 or 1. The size of the generated executable file is quite different.
As mentioned above, the initialization of global variables is the same as that of non-initialization. Therefore, the Test1 case does not initialize global variables.
So what is the impact of initialization of global variables on the code space?
We know that data and programs are stored together in programs running on the von noriman-based architecture system. Therefore, during compilation, the compiler binds the initialization data of global variables to the final generated program file. For uninitialized global variables, it only allocates (instructs) storage locations for them, it will not bind a large number of zeros to the program.
Now let's look at the above two cases. Test1 does not initialize the global variable. during compilation, the compiler only points out the memory location to be used for init_array_g1 [] without data binding. Test2 is different. It initializes init_array_g1 [0] to 1, and all other elements are initialized to 0. Therefore, the compiler binds all the initialization data of the 10000000 elements in the init_array_g1 [] array to the final executable file, resulting in a huge size of the compiled file.
3.3 about heap and stack
For historical reasons, we are used to combining heap and stack. However, here we need to strictly differentiate the concept of heap and stack.
The local variables declared in the routine are allocated to the stack, and the stack size is quite limited (1 or 2 MB). A large array may make the stack insufficient, this results in an Overflow error during runtime (Note: it is not a compiler error). The size of the heap mainly depends on the amount of available system memory and virtual memory. Here are several examples:
Example 3 code is shown in table 4.5:
Compile the code without any compilation errors. During execution, a runtime error occurs (Statck Overflow prompt) because the stack space is insufficient.
Case 4: Modify case 3 code and locate the array as a global variable, as shown in table 4.6:
Compile the code without any compilation errors or any running errors. Because the global variables are not allocated to the stack (Note: they are not in the heap), the amount of space that can be used depends on the amount of available system memory and virtual memory.
There is another way to solve the problem in Case 3: dynamically applying for memory space.
The dynamically applied memory space is allocated at runtime. Once the application is successful, it is allocated to the heap. Therefore, the size depends on the available memory and virtual memory of the system.
Case 5: Use another method to modify case 3 code, as shown in table 4.7.
Case 5 memory space is in the heap. Another difference is that in case 4, the memory space in case 4 is allocated by the compiler, while in case 5, the memory space is allocated at runtime and may not be allocated.
3.4) Address decline Compilation Method
Other documents may already describe the concept of "address decrease" Addressing Method for memory allocation. The so-called "address decrease" means that when the compiler compiles a program, it is declared successively by variable, allocate memory from the high address to the low address in the allocable memory. What does it mean? Let's look at an example first.
Case 6 is a program with a logic error (as shown in table 4.7). It may be called a "abnormal" program. So how is it BT?
This program has no compiler error, but it is an endless loop program. What we want to know is: Why is it an endless loop, not other errors? With the introduction of memory layout, we can easily explain it.
The memory layout can be drawn based on the content in section 3rd (as shown in section 4.1, the starting address in the figure is only a typical situation ).
Note that the program references array [10] ---- array subscript out of bounds (VC ++ 6.0 compiler can check that the displayed subscript is out of bounds, but the implicit subscript out of bounds is not checked ). In the loop, the so-called array [10] is set to 1. As shown in Figure 4.1, array [10] is essentially I, and the final endless loop of the program is taken for granted.
Everything becomes clearer. We not only explained the problems in the program, but also understood that the address reduction method is not mysterious. It turns out to be the addressing method of the stack memory area we mentioned earlier.