1024 Programmers Day Special | C++ Getting Started Guide: Memory Management (recommended to collect!!)



1. C/C++ memory distribution

C/C++ memory distribution is roughly as follows:

area name use
stack (stack) Store non-static local variables/function parameters/return values, etc., and the stack grows downwards
memory mapped segment Efficient I/O mapping method for loading a shared dynamic memory library. Users can use the system interface to create shared shared memory for inter-process communication.
heap Due to dynamic memory allocation while the program is running, the heap can grow.
data segment Store global data and static data
code snippet Executable code/read-only constants

1.1 Related examples

code show as below:

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

Analysis:
Insert image description here
Question:

  1. Multiple choice question:
    Options: A. Stack B. Heap C. Data segment (static area) D. Code segment (constant area)
    Where is globalVar? ____ Where is staticGlobalVar? ____
    Where is staticVar? ____ Where is localVar? ____
    Where is num1? ____
    Where is char2? ____ Where is *char2? ___
    Where is pChar3? ____ Where is *pChar3? ____
    Where is ptr1? ____ Where is *ptr1? ____
    Answer: C, C, C, A, A ;
    A, A, A, D, A, B
  1. What is the difference between sizeof and strlen?
    sizeof is an operator used to get the size of an object or type in bytes. It can be used with any type of variable, including strings.
    strlen is a function that gets the length (in characters) of a string terminated by the null character (‘\0’). It can only be used with strings, not other types of variables.

2. Dynamic memory management method in C language: malloc/calloc/realloc/free

void Test ()
{
    
    
	int* p1 = (int*) malloc(sizeof(int));
	free(p1);
	// 1.malloc/calloc/realloc的区别是什么?
	int* p2 = (int*)calloc(4, sizeof (int));
	int* p3 = (int*)realloc(p2, sizeof(int)*10);
	// 这里需要free(p2)吗?
	free(p3 );
}

2.1 Related interview questions

[Interview questions]:

  1. The difference between malloc/calloc/realloc?
    Analysis: malloc is used to allocate a memory block of a specified size; calloc is used to allocate a specified number and size of contiguous memory blocks and initialized to 0; realloc is used to reallocate the size of the allocated memory block .

2.The implementation principle of malloc?
Analysis: Principle of malloc implementation in glibc


3. C++ memory management method

The C language memory management method can continue to be used in C++, but it is powerless in some places, and it is more troublesome to use (custom types), so C++ has proposed its own memory management method:< a i=1>Dynamic memory management through new and delete operators.

3.1 New/delete operation built-in types

void Test()
{
    
    
	// 动态申请一个int类型的空间
	int* ptr4 = new int;

	// 动态申请一个int类型的空间并初始化为10
	int* ptr5 = new int(10);

	// 动态申请10个int类型的空间
	int* ptr6 = new int[3];
	delete ptr4;
	delete ptr5;
	delete[] ptr6;
}

Encyclopedia:
Insert image description here
Small tips:

  1. To apply for and release space for a single element, use the new and delete operators. To apply for and release continuous space, use new[] and delete[].
  2. Compared with malloc, new does not require forced transfer and will automatically calculate the size. In addition, additional initialization is supported! !

3.2 New and delete operations on custom types

Here are some common uses of new and delete custom types:

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

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

private:
	int _a;
};

int main()
{
    
    
	//malloc没有办法很好的支持自定义类型动态申请空间的初始化-->new
	A* p1 = (A*)malloc(sizeof(A));
	//p1->A(1); 创建对象时,自动调用,不可显示调用

	//new = malloc + 构造函数 (申请空间+ 初始化)
	A* p2 = new A;
	A* p3 = new A(1);
	//delete = 析构函数 + free
	delete p2;
	delete p3;

	A* p4 = new A[10];
	delete[] p4;

	A a1(1);
	A a2(2);
	A* p5 = new A[10]{
    
     a1,a2 };
	delete[] p5;

	//匿名对象
	A* p6 = new A[10]{
    
     A(1), A(2) };
	delete[] p6;

	//隐式类型转换
	A* p7 = new A[10]{
    
     1,2 };
	delete[] p7;
	return 0;
}

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


4. Operator new and operator delete functions

new and delete are operators for users to apply for and release dynamic memory, operator new and operator delete are global functions provided by the system /span> global function to release space. delete is The bottom layer uses the operator delete global function at the bottom to apply for space, new calls operator new,

Operator new and operator delete are essentially encapsulations of malloc and free, except that operator new will not return a null pointer if it fails to apply for space, but will throw an exception.

The specific packaging is as follows:

//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)

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 will return directly. Otherwise, the user-provided countermeasures for insufficient space will be executed. If the user provides this measure, it will continue to apply, otherwise it will Throw an exception. operator delete finally releases space through free.

4.1 Operator new throws exception demonstration

Next, open a large enough space and catch exceptions through try...catch.

void func()
{
    
    
	char* ptr = new char[0x7fffffff];
	cout << (void*)ptr << endl;
}

int main()
{
    
    
	try
	{
    
    
		//char* ptr = new char[0x7fffffff];
		//*cout << ptr << endl;*//对于char*, cout不会按指针打印,会按照字符串打印。
								//同时由于编码等问题,会报屯屯...
		//cout << (void*)ptr << endl;

		func();
		cout << "hello C++" << endl;
	}
	catch (const exception& e)
	{
    
    
		cout << e.what() << endl;
	}

	return 0;
}

Capture unusual results:
Insert image description here


5. Implementation principles of new and delete

5.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, while 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.

5.2 Custom types

  1. New principle
    . 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.

  2. 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

  3. 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 for N object spaces.
    . 2. Execute the constructor N times on the requested space.

  4. Principle of delete[]
    . 1. Execute N destructors on the released object space to complete the cleanup of resources in N objects.
    . 2. Call operator delete[] to release space. Actually, operator delete is called in operator delete[] to release space.

5.3 Related examples

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

private:
	int _a;
};
int main()
{
    
    
	A* p2 = new A[10];
	delete p2;
	return 0;
}
int main()
{
    
    
	//Stack* ptr = (Stack*)operator new(sizeof(Stack));
	//operator delete(ptr);

	A* p2 = new A[10];
	//delete[] p2;
	//free(p2);
	delete p2;
	return 0;
}

Tips: If the destructor is written without display, the member will run normally. If the destructor is displayed, an error will be reported.
Analysis:


6. Positioning new expression (placement-new)

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

Use format:
new (place_address) type or new (place_address) type(initializer-list)
place_address must be a pointer, initializer-list is the initialization list of the type

Usage scenario:
Positioning new expression is generally used in conjunction with the memory pool in practice. Because the memory allocated by the memory pool is not initialized, if it is a custom type object, you need to use the definition expression of new to explicitly call the constructor for initialization.

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

// 定位new/replacement new
int main()
{
    
    
	 // p1现在指向的只不过是与A对象相同大小的一段空间,还不能算是一个对象,因为构造函数没有执行
	 A* p1 = (A*)malloc(sizeof(A));
	 new(p1)A;  // 注意:如果A类的构造函数有参数时,此处需要传参
	 p1->~A();
	 free(p1);
	 
	 A* p2 = (A*)operator new(sizeof(A));
	 new(p2)A(10);
	 p2->~A();
	 operator delete(p2);
 	 return 0;
}

7. 7. Common interview questions

7.1 The difference between malloc/free and new/delete


What malloc/free and new/delete have in common is that they both apply for space from the heap and require the user to release it manually. The difference is:
5. malloc and free are functions, new and delete are operators.
6. The space requested by malloc will not be initialized; new can be initialized.
7. When malloc applies for space, you need to manually calculate the space size and pass it; new only needs to be followed by the type of space. If there are multiple objects, specify the number of objects in [] That’s it.
8. The return value of malloc is void*, which must be forced when used; new does not need to, because new is followed by the type of space.
9. When malloc fails to apply for space, it returns NULL, so it must be null when used; new does not need to, but new needs to catch exceptions.
10. 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. , delete will call the destructor to clean up the resources in the space before releasing the space.

7.2 Memory leak

7.2.1 What is a memory leak and the dangers of a memory leak?


What is a memory leak: A memory leak refers to a situation where a program fails to release memory that is no longer used due to negligence or error.
Memory leaks do not mean the physical disappearance of memory, but that after the application allocates a certain segment of memory, it loses control of the segment of memory due to design errors, thus causing memory corruption. waste.

Hazards of memory leaks: Memory leaks occur in long-running programs, which have a great impact, such as operating systems, background services, etc.< a i=5> Memory leaks will cause the response to become slower and slower, and eventually freeze.

7.2.2 Memory leak classification

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

  1. Heap memory leak (Heap leak)
    Heap memory refers to the memory allocated from the heap through malloc / calloc / realloc / new, etc. during program execution according to needs. memory not to be released, then this part of the space will no longer be able to be used, and a Heap Leak will occur.
    block of memory must be deleted by calling the corresponding free or delete after use. Assume that the design error of the program causes this part of

  2. System resource leakage
    means that the program uses resources allocated by the system, such as sockets, file descriptors, pipes, etc. without using corresponding functions to release them
    is lost, resulting in a waste of system resources, which may seriously lead to reduced system performance and unstable system execution.

7.2.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 a>Approximately how many bytes were leaked, there is no other more accurate location information.

int main()
{
    
    
	 int* p = new int[10];
	 // 将该函数放在main函数之后,每次程序退出的时候就会检测是否存在内存泄漏
	 _CrtDumpMemoryLeaks();
	 return 0;
}

	// 程序退出后,在输出窗口中可以检测到泄漏了多少字节,但是没有具体的位置
	Detected memory leaks!
	Dumping objects ->
	{
    
    79} normal block at 0x00EC5FB8, 40 bytes long.
	Data: <                > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD
	Object dump complete.

Therefore, you must be careful when writing code, especially when operating dynamic memory, you must remember to release. But in some cases, it is always difficult to prevent it. You can simply use the above method to quickly locate it. If the project is relatively large and there are many memory leak locations, which are difficult to detect, it is usually handled with the help of third-party memory leak detection tools.

7.2.4 How to avoid memory leaks

  1. Establish good design standards in the early stage of the project, develop good coding standards, and remember to release the allocated memory space in a matching manner. ps: This is the ideal state. But if you encounter an exception, even if you pay attention to the release, problems may still occur. The next smart pointer is needed to manage it to ensure it.
  2. Use RAII ideas or smart pointers to manage resources.
  3. Some companies' internal specifications use internally implemented private memory management libraries. This library comes with built-in memory leak detection options.
  4. Use memory leak tools to detect when something goes wrong. ps: However, many tools are unreliable or expensive.

To summarize:
Memory leaks are very common, and the solutions are divided into two types: 1. Preventive type. Such as smart pointers, etc. 2. Check for errors afterwards. Such as leak detection tools.

Guess you like

Origin blog.csdn.net/Zhenyu_Coder/article/details/133956448