[C++] C&C++ memory management

1. C/C++ memory distribution

Let's first look at such a piece of code

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);
}

Do you know what area of ​​memory each variable in the above code is located in?

image-20230410172141657

illustrate

  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
  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 and static data
  5. Code Segment – ​​executable code/read-only constants

Explain here why the stack grows downward and the heap grows upward

image-20230411103202212

When applying for space, the space that the stack first applies for is a high address, and the space that is applied later is a low address. The space that the heap applies for first is a low address, and the space that is applied later is a high address. Therefore, the stack grows downward, and the heap grows upward . to grow

Look at the following piece of code:

void Test_stack()
{
     
     
    //在栈区开辟a,b两个变量,先开辟的变量地址高
	int a = 10;
	int b = 20;
	cout << "&a=" << &a << endl;
	cout << "&b=" << &b << endl;
    //在堆区开辟aa和bb,先开辟的地址低
	int* aa = (int*)malloc(sizeof(int) * 2);
	int* bb = (int*)malloc(sizeof(int) * 2);
	cout << "aa=" << aa << endl;
	cout << "bb=" << bb << endl;
}

Note: The address opened first in the heap area is not necessarily lower than the address opened later, and it is also possible to open the memory that was released before.

2. The management method of dynamic memory in C language

In C language, dynamic memory is managed through four functions, namely malloc, realloc, calloc, free

1.malloc

The malloc function opens up a continuous address space of a specified size in the memory, and returns the first address of the space, or returns NULL if the opening is unsuccessful

2.calloc

The function of the calloc function is also to open up a continuous address space of a specified size in the memory, and return the first address of the space. In the process of opening, the initial value of each byte can be given. If the opening is unsuccessful, NULL will be returned.

3.realloc

The function of the realloc function is to modify the dynamically allocated memory size and return the first address of the modified memory address space.

4.free

The function of the free function is to release the dynamically allocated memory

For more details, please see the following blog: [Advanced C language] Dynamic memory management_Xiao Zhang is trying to write code blog-CSDN blog

3. Dynamic memory management in C++

In C++, the memory management method provided in C language can also be used, but in some cases, when developing a custom type, some problems will occur when using functions such as malloc, so in C++, a new memory management method is used ----- new and delete

1.new/delete operation built-in type

void Test_new_delete_Built()
{
    
    
	//操作内置类型
	//动态申请一个整型大小的空间
	int* ptr1 = new int;
	//动态申请一个整型大小的空间,并初始化为10
	int* ptr2 = new int(10);
	//动态申请10个整型大小的空间
	int* ptr3 = new int[10];
	//对于new申请的空间,使用delete释放
	delete ptr1;
	delete ptr2;
	//对于new[]申请的空间,使用delete[]释放
	delete[] ptr3;
}

The difference between new and malloc

For malloc: failure to open space returns a null pointer

image-20230411142156533

For new: failure to open up space directly throws an exception, no need to check the return value

image-20230411143952368

2. new/delete operation custom type

The real difference between new/delete and malloc/calloc/realloc/free is the custom type

class A
{
    
    
public:
	A(int* a = nullptr)
		:_a(a)
	{
    
    
		cout << "A()" << this << endl;
	}
	~A()
	{
    
    
		cout << "~A()" << this << endl;
	}
private:
	int* _a;
};

void Test_Custom()
{
    
    
	cout << "malloc:" << endl;
	A* pA1 = (A*)malloc(sizeof(A));
	cout << pA1 << endl;
	cout << "------------------------------" << endl;
	free(pA1);
	cout << "new:" << endl;
	A* pA2 = new A();
	delete pA2;
}

image-20230411145247719

It can be seen that the difference between new/delete and malloc and other C language interfaces is that new and delete call the default constructor and destructor of the self-defined type, while malloc and other interfaces do not call, and only play the role of memory management.

Summarize:

1. When managing memory of built-in types, there is no difference between using new/delete and malloc/calloc/realloc/free

2. When managing the memory of a custom type, new is used to open up memory + call the default constructor, and delete is used to release memory + call the destructor

3. Use delete to release the space created by using new, and use delete[] to release the space opened by using new[]

4. 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. 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 calls the operator new global function at the bottom layer to apply for space, and delete uses the operator delete global function at the bottom layer to release space

The underlying mechanism of new

  1. operator new --> malloc
  2. call the constructor
class A
{
    
    
public:
	A(int* a = nullptr)
		:_a(a)
	{
    
    
		cout << "A()" << this << endl;
	}
	~A()
	{
    
    
		cout << "~A()" << this << endl;
	}
private:
	int* _a;
};
void Test()
{
    
    
	A* p = new A;
}

image-20230411185711283

By looking at the assembly code, you can see that this operation of new is done: 1. Call operator new 2. Call the constructor of the custom type


Note: operator new looks like operator overloading, however, in fact operator new is not operator overloading because there is no custom type in that function parameter

//operator new:该函数实际通过malloc来申请空间,当malloc申请空间成功时直接返回;申请空间失败,尝试执行空 间不足应对措施,如果改应对措施用户设置了,则继续申请,否则抛异常。
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);
}

//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 );
    __FINALLY
    	_munlock(_HEAP_LOCK); // release other threads
    __END_TRY_FINALLY
    return;
}

//free的实现
#define free(p) _free_dbg(p, _NORMAL_BLOCK)

The above code may be a bit difficult to understand. Simply speaking, the malloc function is encapsulated in operator new, and the returned NULL after malloc fails is encapsulated as throwing an exception, and the countermeasures provided by the user are executed. operator delete eventually releases space through free.

5. Implementation principle of new and delete

1. Built-in types

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

2. Custom type

1. The principle of new

1. 调用operator new函数在内存中申请空间
1. 在申请的空间上执行该类型的构造

2. The principle of delete

1. 在空间上执行该类型的析构函数
1. 调用operator delete函数释放空间

3. The principle of new T[N]

1. 调用operator new[]函数,operator new[]函数实际上就是封装了operator new函数,执行N次operator new
1. 在申请的空间上执行N次该类型的构造函数

4. The principle of delete[]

1. 在申请的空间上执行N次析构函数
1. 调用operator delete[]函数,operator delete[]实际上就是封装了operator delete函数,执行N次operator delete[]

6. Positioning new expression (placement-new)

The function of positioning the new expression is to call the constructor to initialize an object in the allocated original memory space

use format

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

Among them, place_address must be a pointer, and initializer-list is the initialization list of the type

scenes to be used

class A
{
     
     
public:
	A(int a = 5)
		:_a(a)
	{
     
     
		cout << "A()" << this << endl;
	}
	~A()
	{
     
     
		cout << "~A()" << this << endl;
	}
private:
	int _a;
};
void Test_replacement_new()
{
     
     
	//new (place_address) type
	A* p1 = (A*)malloc(sizeof(A));
	if (p1 == nullptr)
	{
     
     
		perror("malloc fail");
		exit(-1);
	}
	new(p1)A();
	p1->~A();
	free(p1);
	//new (place_address) type(initializer-list)
	A* p2 = (A*)operator new(sizeof(A));
	new(p1)A(10);
	p2->~A();
	free(p2);
}

Note: No matter after executing malloc or executing operator new, p1p2 is just a space with the same size as object A, not an object, because the constructor has not been executed yet

7. Common Interview Questions

1. Comparison between malloc/free and new/delete

Consider from two aspects of usage function and bottom layer

  • common ground:

    All apply for space from the heap and need to be released manually

  • difference

    1. malloc/free are functions, new/delete are operators
    2. The space applied by malloc will not be initialized, and the space applied by new will call the constructor to initialize
    3. The malloc application space needs to manually pass the number of bytes of the space to be opened up, and the new application space only needs to pass the number of types
    4. The return value type of malloc is void*, which needs to be used after forced conversion, and new does not need it
    5. Malloc application fails to return NULL, when it is used, it needs to perform a null operation, and new application fails to throw an exception, which needs to be caught
    6. malloc and free will only open up space and will not operate on the space. New and delete will automatically call the constructor and destructor on the basis of completing the functions of malloc and free.
    7. The bottom layer of new is also implemented by encapsulating malloc, and the bottom layer of delete is also implemented by encapsulating free.

2. Memory leaks

1. What is a memory leak and the harm of a memory leak

What is a memory leak : A memory leak refers to a program that fails to release unused memory for some reason. A memory leak is not a physical disappearance, but a memory waste caused by the operating system losing control of the part of the memory after it allocates the memory to the program, causing the operating system to find the memory.

Hazards of memory leaks : Memory leaks occur in long-running programs, which have a great impact, such as operating systems, background services, etc. Memory leaks will lead to slower and slower responses and eventually freeze

2. Classification of memory leaks

In C/C++ programs, we generally care about two aspects of memory leaks:

  • Heap memory leak (Heap leak)
    Heap memory refers to a piece of memory allocated from the heap through malloc / calloc / realloc / new, etc. during program execution as needed, and must be deleted by calling the corresponding free or delete after use. Assuming that the design error of the program causes this part of the memory to not be released, then this part of the space will no longer be used in the future, and Heap Leak will occur.
  • System resource leakage
    refers to the resources allocated by the system used by the program, such as sockets, file descriptors, pipes, etc., which are not released using the corresponding functions, resulting in waste of system resources, which can seriously reduce system performance and cause unstable system execution

3. How to detect memory leaks

Under vs, you can use the _CrtDumpMemoryLeaks() function provided by the windows operating system for simple detection. This function only reports the approximate number of bytes leaked, and there is no other more accurate location information

image-20230411210926847

Therefore, you must be careful when writing code, especially when operating dynamic memory, you must remember to release it. However, in some cases, it is always impossible to prevent, and the simple method can be used to quickly locate it. If the project is relatively large and there are many memory leak locations, it is usually handled with the help of third-party memory leak detection tools when it is not easy to check.

4. How to avoid memory leaks

  1. Good design specifications in the early stage of the project, develop good coding standards, and remember to release the memory space that matches. ps: This ideal state. But if you encounter an exception, even if you pay attention to release, there may still be problems. It needs to be managed by the next smart pointer to be guaranteed.
  2. Use RAII ideas or smart pointers to manage resources.
  3. Some company internal specifications use internally implemented private memory management libraries. This library comes with the function of memory leak detection.
  4. Something went wrong using a memory leak tool to detect. ps: However, many tools are not reliable enough, or the fees are expensive.

Memory leaks are very common, and there are two solutions:

  1. preventive type. such as smart pointers
  2. After the fact, check the error type. such as leak detection tools

Guess you like

Origin blog.csdn.net/weixin_63249832/article/details/130093986