C++ dynamic memory allocation, memory overflow/leakage, wild pointer/dangling pointer and code examples

Recently, bloggers have learned about dynamic memory allocation in C++. The learning content includes: why dynamic memory allocation is needed, how to perform dynamic memory allocation, C++ memory mechanism (the difference between heap and stack), memory overflow/leakage, wild pointer/dangling pointer, combined code example analysis, etc.

At the same time, we have also borrowed a lot of excellent articles, some of which are no longer available for reference. If there is any infringement, we will notify you to delete it.


1. Dynamic memory allocation

1. Why do you need dynamic memory allocation?

Sometimes, how much memory a program needs cannot be predicted in advance, but only known when the program is running. In this case, it is not possible to define all variables in advance, but it is necessary to allocate memory artificially and dynamically when the program is running.

2. How to perform dynamic memory allocation?

new/delete operator.

(1) Dynamic memory allocation of simple variables

#include <iostream>
#include <windows.h>
using namespace std;

int main ()
{
    
    
    int *p1 = new int(1);
    float *p2 = new float(2.0f);
    char *p3 = new char('c');
    /*
    或者上述代码也可以写成:
	int *p1 = new int;
	*p1 = 1;
	*/

    cout<<*p1<<endl;
    cout<<*p2<<endl;
    cout<<*p3<<endl;
    cout<<p1<<endl;
    cout<<p2<<endl;
	cout<<p3<<endl;
	delete p1;
	delete p2;
	delete p3;

    cout<<"\n"<<endl;
    system("pause"); 

   return 0;
}

Running result:
insert image description here
However, the space in the computer is limited. If the space is used up and the memory cannot be allocated, what should be done? You can use the following piece of code to check whether the memory allocation was successful:

double* pvalue  = NULL;
if( !(pvalue  = new double ))
{
    
    
   cout << "Error: out of memory." <<endl;
   exit(1);

}

(2) Dynamic memory allocation of arrays

#include <iostream>
#include <windows.h>
using namespace std;

int main ()
{
    
    
    int* array = new int[5];  //分配内存
    for(int i = 0 ; i < 5 ; i++){
    
    
        array[i] = i;
        cout<<*(array+i)<<endl;  //这里也可以写成array[i]
    }
    delete [] array;  //删除内存
    
    cout<<"\n"<<endl;
    system("pause"); 
    return 0;
}

operation result:
insert image description here

(3) Dynamic memory allocation of objects (omitted)


Two, C++ memory mechanism

1. Stack area

Main storage : function parameters, local variables.
When allocating memory, find empty memory in the stack area from high address to low address for allocation.
All variables declared inside the function will use stack memory.调用函数时,分配内存,函数结束时,内存自动释放。每调用一个函数就会给它分配一段连续的栈空间。

2. Heap area

Main storage : Programmers use new and malloc memory.
When allocating memory, look for an empty memory area from the low address to the high address in the heap area of ​​the memory for allocation.
Use delete, free to release memory.

3. Global zone/static zone

Main storage : global variables in the program, static static variables.
When allocating memory, find a free memory area in the global area from low address to high address for allocation.

4. Constant area

Store string constants and integer constants.

5. Code area

Store the cpu instructions executed by the program.

insert image description here

Supplement 1: In-depth understanding of the usage characteristics of the stack area and the heap area

1.	#include <stdio.h>
2.	#include <stdlib.h>
3.	 
4.	int main() {
    
    
5.	  int a;
6.	  int *p;
7.	  p = (int *)malloc(sizeof(int));
8.	  free(p);
9.	 
10.	  return 0;
11.	}

The above code is very simple. There are two variables a and p, whose types are int and int * respectively. Among them, a and p are stored on the stack, and the value of p is an address on the heap (in the above code, p’s The value is 0x1c66010), the layout of the above code is shown in the figure below:

insert image description here

Supplement 2: The difference between the heap and the stack

insert image description here
insert image description here

3. Memory leaks, memory overflows, wild pointers, dangling pointers

memory leak

Memory leak: A certain part of memory should be released, but it is not released due to various reasons, resulting in waste of memory.

Case of memory leak:
Cause of error: The memory requested by malloc and new is not actively released.

#include <iostream>
#include <windows.h>
using namespace std;

int main ()
{
    
    
    while(true){
    
    
        int *s = (int*)malloc(50); 
    }
    cout<<"\n"<<endl;
    system("pause"); 

   return 0;
}

#include <iostream>
using namespace std;
int main()
{
    
     
   int *a = new int(123);
   cout << *a << endl;
   // We should write "delete a;" here
   a = new int(456);
   cout << *a << endl;
   delete a;
   return 0;
}

Note:
(1) The temporary variable pointer defined inside the function is like other variables, and will be released automatically when the function ends, but the new memory space will not be released, 通过new分配的内存一定要手动释放.
(2) When a pointer is defined but not assigned a value, the pointer is NULL. 对空指针应用delete是安全的.

out of memory

Memory overflow: The system can no longer allocate the space you need. For example, the system currently only has 1G space, but you want 2G space, this is called memory overflow.

Memory leaks will eventually lead to memory overflow.

wild pointer

Simply put: uninitialized pointer
Simply put, it refers to a pointer to an unusable memory area. The wild pointer points to the pointer of the garbage memory area. Once used, it will often cause unpredictable results. This kind of random and unpredictable results is the most terrible.

for example:

void func() {
    
    
    int* p;
    cout<<*p<<endl;
}

As a result p will output a random number.

Dangling pointer:

To put it simply: if a pointer object is deleted, it becomes a dangling pointer.
Dangling pointers are also a common type of wild pointers. When we explicitly delete an object from memory or return by destroying the stack frame, the value of the associated pointer is not changed. This pointer actually still points to the same location in the memory, and even the location can still be read and written, but at this time the memory area is completely uncontrollable, because you cannot guarantee whether this memory has been used by other programs or codes.

for example:

void dangling_point() {
    
    
    char *p = (char *)malloc(10);
    strcpy(p, "abc");
    cout<<p<<endl;
    free(p);
    if (p != NULL) cout<<p<<endl;
    strcpy(p, "def"); 
}

When we execute the free statement, the memory pointed to by p is released, and the memory area becomes unusable memory at this time. However, the address pointed to by p at this time has not changed, and it will not be empty, so if (p != NULL) is judged to be true, the following statements will continue to be executed.
However, strcpy(p, "def"); Although the code will not report an error and crash, it is tampering with the dynamic memory area at this time, which will produce unpredictable results. This operation is quite dangerous, especially when debugging, it is very difficult to troubleshoot.

Solution: In order to avoid the problem of dangling pointers, the general practice is to set p=NULL after free/delete pointers.


4. A practical case

Look at the following two pieces of code and think about the difference in their output.
one:

#include <iostream>
#include <windows.h>
using namespace std;

class People{
    
    
    public:
        string name;
        long birthday;

};

People* getPeople(){
    
    
People px;
px.birthday = 1111;
    return &px;      
    /*
    返回&px程序会出现警告:
    address of local variable 'px' returned [-Wreturn-local-addr]gcc
    原因:函数内部定义的变量在函数结束时会被释放掉,所以返回一个地址是不行的。
    */
}

int main ()
{
    
    
    People *p = getPeople();
    cout<<p->birthday<<endl;
    system("pause"); 

   return 0;
}

two:

#include <iostream>
#include <windows.h>
using namespace std;

class People{
    
    
    public:
        string name;
        long birthday;

};

People getPeople(){
    
    
    People px;
px.birthday = 1111;
return px;
}

int main (){
    
    
    People p = getPeople();
    cout<<p.birthday<<endl;
    system("pause"); 
    return 0;
}

At first glance, there is no difference between the two, but a warning will appear in the first program: address of local variable 'px' returned [-Wreturn-local-addr] gcc The reason
insert image description here
: the code is one, and the return is an address; What is returned in code 2 is an actual value.

We know that the local variables in the custom function are stored in the stack area, the memory space is automatically allocated when the function starts running, and the memory space is automatically released when the function finishes running .

At this time, it is not allowed to return the address of an internal variable in the function . Because after the function runs, these temporary memory spaces are released, and the variables disappear. Although I have returned the address, I can only find loneliness with this address in the future to find the memory space that has disappeared.

At this time, it is possible to return the value of a variable in the function . Because what we return is a real data, we pass the data over, and the value is received there.

References

1. warning: function returns address of local variable【The reason why the function returns the first address error】
2. Summary of c++ wild pointer
3. C/C++ memory leak-cause, avoidance and location
4. 【C++】Detailed explanation of dynamic memory allocation (new/new [] and delete/delete[])

Guess you like

Origin blog.csdn.net/rellvera/article/details/129530598