Hello, I’m Liang Tang.

This is part 18 of the EasyC++ series on memory models in C++.

If you want a better reading experience, you can visit the Github repository.

Introduction to C++ memory model

Create the structure dynamically

We have described the use of the new operator to dynamically create arrays. The new operator can also be used to dynamically create structures and class objects. Also, unlike declaration, dynamically created memory is stored in heap memory and is more flexible.

Suppose we define a structure like this:

struct P {
	int x, y;
};
Copy the code

We can create an object like this:

P *p = new P;
Copy the code

But there is a question, how do we access the member variables in the structure? We know that if an object is created by a declared method, we can pass. Operator to access:

P p;
p.x = 3;
Copy the code

For Pointers, we have a new operator written as ->, which is the arrow symbol. We can use the arrow symbol to access the element in a pointer structure:

P *p = new P;
p->x = 3;
Copy the code

We can also dereference the pointer, which gives us an instance of a structure, which we can use directly. To access. But there’s no need to do that, especially if you’re a beginner and you can get confused. So if it’s cute, remember to use the arrow symbol for Pointers, and use the arrow symbol for non-pointers. Can.

P *p = new P;
// p dereference the instance, ready to use.
(*p).x = 3;
Copy the code

Principle of Memory Allocation

Memory allocation in C++ is divided into three main types: automatic storage, static storage, and dynamic storage. Thread storage has been added to C++11, which we will discuss later.

Automatic storage

Automatic storage can be understood as local variables and refers to general variables defined inside a function, also known as automatic variables. They are created automatically when the function is called and die when the function ends.

void test(a) {
    int x;
    return ;
}
Copy the code

For example, if we create a variable x of type int in the test function, the memory of the variable will be freed when the function call ends.

The scope of an automatic variable is the block of code that contains it, that is, the piece of code enclosed in curly braces. In fact, it does not only contain functions, but also loop, branch statements, and so on.

Automatic variables are usually stored on the stack and are added to the stack as we execute code. When the code blocks leave, they are released in reverse order. For those of you who have studied operating systems, you know that this memory model is called LIFO.

Static storage

Static storage refers to storage that exists throughout the execution of a program. There are two ways to call a variable static. One is to define it outside the function and make it a global variable. The other is to use the keyword static when declaring variables.

static int a = 30;
Copy the code

To put it simply, variables decorated with the static keyword are initialized only once, even if the function is called repeatedly. Sometimes we want some intermediate results to be saved when a function is called multiple times. In general, to do this, we need to define global variables. However, the domain of global variables is no longer the function itself. If we want the scope of a variable to remain limited to the function itself, we can use the static keyword.

Here’s a simple example:

void fn(a) {
    static int n = 10;
    cout << n << endl;
    n++;
}


int main(a) {
    fn(a);fn(a);fn(a); }Copy the code

We define a function fn, in which we define a static variable n, and perform n++ every time. If n is understood as a dynamic variable inside the function, then we should get 10 every time we call fn, because it is initialized every time. Because we added the static keyword, we only initialized once, even though we called main three times. So the final result is:

Dynamic storage

Dynamic storage is a variable created using new, for which C++ manages a pool of memory called the free store or heap. Heap memory is separate from automatic and static storage. Using the new and delete functions, we can allocate memory in one function and free it in another. Therefore, the life cycle of data is not completely limited by the program or function.

Another point to note is that in the stack, due to the auto-add and auto-remove mechanisms. So the memory is contiguous, whereas in the heap, the use of new and DELETE causes fragmentation, which can make it more difficult to allocate new memory, especially if we have requested a large amount of memory.