Expert C++
上QQ阅读APP看书,第一时间看更新

Memory segments

The memory consists of segments and the program segments are distributed through these memory segments during the loading. These are artificially divided ranges of memory addresses that make it easier to manage the program by the OS. A binary file is also divided into segments, such as code and data. We previously mentioned code and data as sections. Sections are the division of a binary file needed for the linker, which uses the sections that are meant for the proper work of the linker and combines the sections that are meant for the loader into segments.

Basically, when we discuss a binary file from the runtime perspective, we mean segments. The data segment contains all the data required and used by the program, and the code segment contains the actual instructions that process the very same data. However, when we mention data, we don't mean every single piece of data used in the program. Let's take a look at this example:

#include <iostream>
int max(int a, int b) { return a > b ? a : b; }
int main() {
std::cout << "The maximum of 11 and 22 is: " << max(11, 22);
}

The code segment of the preceding program consists of the instructions of the main() and max() functions, where main() prints the message using the operator<< of the cout object and then calls the max() function. What data actually resides in the data segment? Does it contain a and b arguments of the max() function? As it turns out, the only data that is contained in the data segment is the string The maximum of 11 and 22 is:, along with other static, global, or constant data. We didn't declare any global or static variables, so the only data is the mentioned message.

The interesting thing comes with values the 11 and 22. These are literal values, which means they have no address; therefore they are not located anywhere in the memory. If they are not located anywhere, the only logical explanation of how they are located within the program is that they reside in the code segment. They are a part of the max() call instruction. 

What about the a and b arguments of the max() function? And here comes the segment in the virtual memory that is responsible for storing variables that have automatic storage duration— the stack. As already mentioned previously, the stack automatically handles the allocation/deallocation of memory space for local variables and function arguments. The arguments a and b will be located in the stack when the max() function is called. In general, if an object is said to have automatic storage duration, the memory space will be allocated at the beginning of the enclosing block. So when the function is called, its arguments are pushed into the stack:

int max(int a, int b) {
// allocate space for the "a" argument
// allocate space for the "b" argument
return a > b ? a : b;
// deallocate the space for the "b" argument
// deallocate the space for the "a" argument
}

When the function is done, the automatically allocated space will be freed at the end of the enclosing code block.

The enclosing code block represents not only the function body but also the block of the conditional statements and loops. 

It's said that the arguments (or local variables) are popped out of the stack. Push and pop are terms used within the context of the stack. You insert data into the stack by pushing it, and you retrieve (and remove) data out of the stack by popping it. You might have encountered the LIFO term, which stands for last in, first out. That perfectly describes the push and pop operations of the stack.

When the program is run, the OS provides the fixed size of the stack. The stack is able to grow in size and if it grows to the extent that no more space is left, it crashes because of the stack overflow.