[C++ Elementary] V. Memory Management

1. C/C++ memory distribution

C/C++ memory has six areas, we often hear stack , heap , data segment and code segment . The other two are kernel space and memory-mapped segments.
Here are a few instructions:

  1. The stack is also called the stack - - - non-static local variables/function parameters/return values, etc., the stack grows downward .
  2. Memory-mapped segments are efficient I/O mappings for loading a shared dynamic memory bank. Users can use the system interface to create shared shared memory for inter-process communication. (If you haven't learned this in the Linux course, you only need to understand it now)
  3. The heap is used for dynamic memory allocation when the program is running, and the heap can grow upwards .
  4. Data segment - - - stores global data and static data.
  5. Code Segment - - - Executable code/read-only constants

Observe the following piece of code and answer the questions:

int globalVar = 1;
static int staticGlobalVar = 1;
void Test()
{
    
    
	static int staticVar = 1;
	int localVar = 1;
 
	int num1[10] = {
    
     1, 2, 3, 4 };
	char char2[] = "abcd";
	const char* pChar3 = "abcd";
	int* ptr1 = (int*)malloc(sizeof(int) * 4);
	int* ptr2 = (int*)calloc(4, sizeof(int));
	int* ptr3 = (int*)realloc(ptr2, sizeof(int) * 4);
	free(ptr1);
	free(ptr3);
}
 
1. 选择题:
 
    选项: A.栈 B.堆 C.数据段 D.代码段
 
    globalVar在哪里?____    staticGlobalVar在哪里?____
    staticVar在哪里?____    localVar在哪里?____
    num1 在哪里?____
 
    char2在哪里?____        *char2在哪里?___
    pChar3在哪里?____       *pChar3在哪里?____
    ptr1在哪里?____         *ptr1在哪里?____
 

Do you know where each part of the code is stored in memory?

insert image description here

Answer: CCCAA AAADAB

By the way: Why is it said that the stack grows downward, but the heap grows upward?
insert image description here
To put it simply, under normal circumstances, when space is opened in the stack area, the address of the space opened first is higher, while when space is opened in the heap area, the address of the space opened first is lower.
For example, in the following code, variable a and variable b are stored in the stack area, and pointer c and pointer d point to the memory space of the heap area:

#include <iostream>
using namespace std;
int main()
{
    
    
	//栈区开辟空间,先开辟的空间地址高
	int a = 10;
	int b = 20;
	cout << &a << endl;
	cout << &b << endl;

	//堆区开辟空间,先开辟的空间地址低
	int* c = (int*)malloc(sizeof(int)* 10);
	int* d = (int*)malloc(sizeof(int)* 10);
	cout << c << endl;
	cout << d << endl;
	return 0;
}

insert image description here

Because in the stack area, the address of the space opened first is higher, so the printed address of a is greater than the address of b; when the space is opened in the heap area, the address of the space opened first is lower, so the address of the space pointed to by c is smaller than the address of the space pointed to by d space address.

Note: When space is opened in the heap area, the address of the space opened later is not necessarily higher than the address of the space opened first. Because in the heap area, the space opened later may also be located in a previously released space.

2. Dynamic memory management in C language

malloc、calloc、realloc和free

1. malloc

The function of the malloc function is to open up a memory space of a specified byte size. If the opening is successful, it returns the first address of the space, and if the opening fails, it returns a NULL. When passing parameters, you only need to pass in the number of bytes that need to be developed.

insert image description here
Header file: stdlib.h

Introduction: malloc is a dynamic memory development function provided by the C language. This function applies for a continuous available space from the memory and returns a pointer to this space. The details are as follows:
  ① If the allocation is successful, a pointer to the allocated space will be returned.

  ② If the development fails, a NULL pointer is returned.

  ③ The type of the return value is void*, and the malloc function does not know the type of the allocated space, it is up to the user to decide.

  ④ If size is 0 (open up 0 bytes), the behavior of malloc is undefined by the standard, and the result will depend on the compiler.

Two, calloc

The function of the calloc function is also to open up a memory space of a specified size. If the opening is successful, it returns the first address of the space, and if the opening fails, it returns a NULL. When the calloc function passes parameters, it needs to pass in the number of elements and the size of each element in the allocated memory for storage. After the calloc function has allocated the memory, each byte in the space content will be initialized to 0.

insert image description here
Header file: stdlib.h

Introduction: The function of the calloc function is to open up a space for num elements whose size is size, initialize each byte of the space to 0, and return a pointer to it.

Comparison:
  ① malloc has only one parameter, while calloc has two parameters, which are the number of elements and the size of the elements.

  ② The difference with the function malloc is that calloc will initialize each byte of the requested space to 0 before returning the address.

Verification: calloc initializes memory

malloc opens up memory:

#include <stdio.h>
#include <stdlib.h>
 
int main()
{
    
    
    // malloc
    int* p = (int*)malloc(40); // 开辟40个空间
    if (p == NULL)
        return 1;
    int i = 0;
    for (i = 0; i < 10; i++)
        printf("%d ", *(p + i));
    free(p);
    p = NULL;
 
    return 0;
}

(The result of the operation is 10 random values)

calloc opens up memory:

#include <stdio.h>
#include <stdlib.h>
 
int main()
{
    
    
    // calloc
    int* p = (int*)calloc(10, sizeof(int)); // 开辟10个大小为int的空间,40
    if (p == NULL)
        return 1;
    int i = 0;
    for (i = 0; i < 10; i++)
        printf("%d ", *(p + i));
    free(p);
    p = NULL;
 
    return 0;
}

0 0 0 0 0 0 0 0 0 0

Summary: It shows that calloc will initialize the memory and initialize each byte of the space to 0. If we require the initialization of the requested memory space, we can use the calloc function.

Three, realloc

The realloc function can adjust the size of the dynamic memory that has been opened. The first parameter is the first address of the dynamic memory that needs to be resized, and the second parameter is the new size of the dynamic memory after adjustment. The realloc function is the same as the above two functions. If the allocation is successful, it will return the first address of the allocated memory, and if the allocation fails, it will return NULL.

insert image description here
Header file: stdlib.h

Introduction: The realloc function makes dynamic memory management more flexible. It is used to readjust the size of the memory block pointed to by the ptr allocated by calling malloc or calloc before, and can adjust the size of the dynamically allocated memory. The details are as follows:
  ① ptr is the memory address to be adjusted by the pointer.

  ② size is the new size after adjustment.

  ③ The return value is the adjusted memory starting position, and a null pointer is returned if the request fails.

  ④ On the basis of adjusting the size of the original memory space, the realloc function will also move the data in the original memory to the new space.

There are three situations when the realloc function adjusts the size of the dynamic memory:
1. In-place expansion : there is enough space behind the space to be expanded. At this time, the realloc function directly expands behind the original space and returns the first memory space address (that is, the original first address).
2. Remote expansion : There is not enough space behind the space to be expanded. At this time, the realloc function will find a new memory space in the heap area that meets the requirements, copy the data in the original space to the new space, and Actively release the memory in the original space (that is, return it to the operating system), and return the first address of the new memory space.
3. Expansion failed : there is not enough space behind the space to be expanded, and there is no space in the heap that meets the required memory size. The result is that memory allocation fails and a NULL is returned.

insert image description here

Four, free

The function of the free function is to release the dynamic memory space applied by the malloc, calloc and realloc functions, and the size of the freed space depends on the size of the previously applied memory space.

3. Dynamic memory management in C++

The C language memory management method can continue to be used in C++, but it is helpless in some places, and it is more troublesome to use, so
C++ has proposed its own memory management method: dynamic memory management through new and delete operators.

new/delete operation built-in type

1. Dynamically apply for a single space of a certain type

//动态申请单个int类型的空间
int* p1 = new int; //申请
	
delete p1; //销毁

Its function is equivalent to:

//动态申请单个int类型的空间
int* p2 = (int*)malloc(sizeof(int)); //申请

free(p2); //销毁

2. Dynamically apply for multiple spaces of a certain type

//动态申请10个int类型的空间
int* p3 = new int[10]; //申请

delete[] p3; //销毁

Its function is equivalent to:

//动态申请10个int类型的空间
int* p4 = (int*)malloc(sizeof(int)* 10); //申请	

free(p4); //销毁

3. Dynamically apply for a single space of a certain type and initialize it

//动态申请单个int类型的空间并初始化为10
int* p5 = new int(10); //申请 + 赋值

delete p5; //销毁

Its function is equivalent to:

//动态申请一个int类型的空间并初始化为10
int* p6 = (int*)malloc(sizeof(int)); //申请
*p6 = 10; //赋值

free(p6); //销毁

4. Dynamically apply for multiple spaces of a certain type and initialize them

//动态申请10个int类型的空间并初始化为0到9
int* p7 = new int[10]{
    
    0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; //申请 + 赋值

delete[] p7; //销毁

Its function is equivalent to:

//动态申请10个int类型的空间并初始化为0到9
int* p8 = (int*)malloc(sizeof(int) * 10); //申请
for (int i = 0; i < 10; i++) //赋值
{
    
    
	p8[i] = i;
}

free(p8); //销毁

Note: To apply for and release the space of a single element, use the new and delete operators to apply for and release continuous space, use new[] and delete[], and use them together .

new and delete operations on custom types

For the following custom types:

class A
{
    
    
public:
	A(int a = 0)
		: _a(a)
	{
    
    
		cout << "A():" << this << endl;
	}

	~A()
	{
    
    
		cout << "~A():" << this << endl;
	}

private:
	int _a;
};

1. Dynamically apply for the space of a single class

With new and delete operators:

A* p1 = new A;//申请

delete p1;//销毁

With malloc and free functions:

A* p2 = (A*)malloc(sizeof(A));//申请

free(p2);//销毁

2. Dynamically apply for multiple classes of space

With new and delete operators:

A* p3 = new A[10];//申请

delete[] p3;//销毁

With malloc and free functions:

A* p4 = (A*)malloc(sizeof(A)* 10); //申请

free(p4); //销毁

From the above point of view, the operations of new and delete on custom types may be no different from malloc and free, but they are not:

Note: When applying for a custom type of space, new will call the constructor, delete will call the destructor, but malloc and free will not

insert image description here

Note: To apply for and release the space of a single element, use the new and delete operators to apply for and release continuous space, use new T[] and delete[], must be used together , otherwise various situations may occur

To sum up:
 1. If you are applying for an object or array of built-in types in C++, there is no difference between using new/delete and malloc/free.
 2. If it is a custom type, there is a big difference. new and delete are respectively opening space + constructor, destructor + releasing space, while malloc and free are only opening space and releasing space.
 3. It is recommended to use new and delete as much as possible in C++, whether it is the application and release of built-in types or custom types.

4. The difference between C and C++ when the memory application fails

In an object-oriented language, the way to handle errors is generally to throw an exception, and C++ also requires this (try catch). In a
process-oriented language, the way to handle errors is generally return value + error code

  1. C language
int main()
{
    
    
	size_t sz = 2;
	int* p1 = (int*)malloc(1024 * 1024 * 1024 * sz);
	if (p1 == nullptr)
	{
    
    
		printf("%d\n", errno);// errno-错误码
		perror("malloc fail");
		exit(-1);
	}

	printf("%p", p1);
	free(p1);

	return 0;
}

Running results:
insert image description here
We can find that the memory application failed, and the program returned error code: 12, because the system does not have such a large space.

  1. C++
int main()
{
    
    
	size_t sz = 2;
	int* p1 = new int[1024 * 1024 * 1024 * sz];

	delete[] p1;
	return 0;
}

insert image description here
The program just crashes.

Next, we throw an exception for the error:

int main()
{
    
    
	size_t sz = 2;
	
	char* p1 = nullptr;
	try
	{
    
    
		// 进行检测 有错误就进入catch被铺获,try中错误代码后面也不执行
		p1 = new char[1024 * 1024 * 1024 * sz];
		cout << "检测无误" << endl;
	}
	catch (const exception& e)
	{
    
    
		cout << e.what() << endl;
	}

	printf("%p", p1);
	delete[] p1;
	return 0;
}

insert image description here

5. operator new and operator delete functions

new and delete are operators for users to apply and release dynamic memory . operator new and operator delete are global functions provided by the system . new and delete apply and release space by calling the global functions operator new and operator delete at the bottom layer.

operator new: operator new() is not an overload of the new operator because the parameter has no custom type. It is a global function in the library.
insert image description here

void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc) 
{
    
    
// try to allocate size bytes
    void *p;
    while ((p = malloc(size)) == 0)
         if (_callnewh(size) == 0)
         {
    
    
             // report no memory
             // 如果申请内存失败了,这里会抛出bad_alloc 类型异常
             static const std::bad_alloc nomem;
             _RAISE(nomem);
         }
    return (p);
}

It can be seen from the underlying code that operator new: This function actually applies for space through malloc, and returns directly when the malloc application space is successful; if the space application fails, try to implement the countermeasures for insufficient space. If the countermeasures are set by the user, continue to apply. Otherwise throw an exception.

operator delete:

//operator delete: 该函数最终是通过free来释放空间的
void operator delete(void *pUserData) {
    
    
     _CrtMemBlockHeader * pHead;
     RTCCALLBACK(_RTC_Free_hook, (pUserData, 0));
     if (pUserData == NULL)
         return;
     _mlock(_HEAP_LOCK);  /* block other threads */
     __TRY
         /* get a pointer to memory block header */
         pHead = pHdr(pUserData);
          /* verify block type */
         _ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));
         _free_dbg( pUserData, pHead->nBlockUse );//调用free()
     __FINALLY
         _munlock(_HEAP_LOCK);  /* release other threads */
     __END_TRY_FINALLY
     return; }
//free的实现
#define   free(p)               _free_dbg(p, _NORMAL_BLOCK)

operator delete: This function finally releases space through free

Therefore, when deleting a built-in type or a class object without resources, using delete and free has the same effect. But for objects that have resources that need to be released, using free directly releases the space of the object, but the resources inside the object have not been cleaned up, resulting in memory leaks! In this case, delete must be used.

Summary:
Through the implementation of the above two global functions, we know that operator new actually applies for space through malloc. If malloc successfully applies for space, it returns directly. Otherwise, it executes the countermeasures provided by the user for insufficient space. If the user provides this measure, it continues to apply. Otherwise an exception is thrown. operator delete finally releases space through free.

Call mechanism:
insert image description here

6. Implementation principle of new and delete

built-in type

If you apply for a built-in type of space, new and malloc, delete and free are basically similar, the difference is:
new/delete applies for and releases the space of a single element, new[] and delete[] apply for continuous space, Moreover, new will throw an exception when it fails to apply for space, and malloc will return NULL.

custom type

The principle of new

  1. Call the operator new function to apply for space
  2. Execute the constructor on the requested space to complete the construction of the object

The principle of delete

  1. Execute the destructor on the space to complete the cleanup of resources in the object
  2. Call the operator delete function to release the object's space

The principle of new T[N]

  1. Call the operator new[] function, and actually call the operator new function in operator new[] to complete the application of N object spaces
  2. Execute the constructor N times on the requested space

The principle of delete[]

  1. Execute N times of destructors on the released object space to complete the cleanup of resources in N objects
  2. Call operator delete[] to release space, actually call operator delete in operator delete[] to release space

7. Position new expression (placement-new)

Positioning the new expression is to call the constructor to initialize an object in the allocated original memory space.

Use the format:

new (place_address) type或者new (place_address) type(initializer-list)

place_address must be a pointer, initializer-list is the initializer list of type

Usage scenario:
Positioning new expressions are generally used in conjunction with memory pools in practice, because the memory allocated by the memory pool is not initialized, so if it is an object of a custom type, you need to use positioning new expressions to display and call the constructor for initialization .

//定位new
class A
{
    
    
public:
	A(int a = 0)
		: _a(a)
	{
    
    
		cout << "A():" << this << endl;
	}

	~A()
	{
    
    
		cout << "~A():" << this << endl;
	}

private:
	int _a;
};
int main()
{
    
    
	//new(place_address)type 形式
	A* p1 = (A*)malloc(sizeof(A));
	new(p1)A;


	A* p3 = (A*)malloc(sizeof(A));
	if (p3 == nullptr)
	{
    
    
		perror("malloc fail");
		exit(-1);
	}
	// 定位new -- 对p3指向空间,显示调用构造函数初始化
	//在已分配的原始内存空间中调用构造函数初始化一个对象。
	new(p3)A(1);//new(place_address)type(initializer-list) 形式

	//p3->~A();
	//free(p3);
	delete p3;

	return 0;
}

Note : Before using the positioning new expression to explicitly call the constructor for initialization, the space requested by malloc cannot be regarded as an object. It is just a space with the same size as the A object, because the constructor has not yet been executed.

8. The difference between malloc/free and new/delete

common ground:

 They all apply for space from the heap and need to be released manually by the user.

difference:

1. malloc and free are functions, and new and delete are operators.
 2. The space applied by malloc will not be initialized, but the space applied by new will be initialized.
 3. When malloc applies for space, it needs to manually calculate the size of the space and pass it on. New only needs to follow it with the type of space.
 4. The return value of malloc is void*, which must be forcibly converted when used. New does not need it, because new is followed by the type of space.
 5. When the malloc application fails, NULL is returned, so it must be judged as empty when used, new does not need it, but new needs to catch exceptions.
 6. When applying for a custom type object, malloc/free will only open up space, and will not call the constructor and destructor, while new will call the constructor to complete the initialization of the object after applying for space, and delete will call the parser before releasing the space. The constructor completes the cleanup of resources in the space.

9. Memory Leak

concept:

A memory leak is a situation in which a program fails to free memory that is no longer in use due to inadvertence or error. A memory leak does not refer to the physical disappearance of memory, but rather the loss of control over a certain segment of memory due to a design error after the application allocates a certain segment of memory, resulting in a waste of memory.

Hazards of memory leaks:

Memory leaks occur in long-running programs, which have a great impact, such as the operating system, background services, etc. Memory leaks will cause slower and slower responses and eventually freeze.

Guess you like

Origin blog.csdn.net/m0_58124165/article/details/123465811