Dynamic Memory Space Management

insert image description here

  • welcome to my world ^_^
  • I hope the author's article is helpful to you, please correct me if there are deficiencies, let's learn and communicate together!

Foreword:

I posted a previous article: C language structure . For me, there are still many places that are only one-sided, but the harvest is still great. For example: in order to know the nesting of structures or memory alignment , I read a lot of information, here is my thanks: the little code farmer who buys wine, the blogger is full of dry goods.
It also proves that the knowledge I have learned is not solid enough. Veterans can also communicate with me in the comment area, let's make progress together. When you write a blog, you will find many of your deficiencies. This is also another study of myself. I would like to tell you a sentence I like very much :
—————————————————————————————— take it lightly

What is dynamic memory

  1. In C language, our memory development is performed on the stack. In short, this belongs to static memory:
int i=0;//这是向内存开辟了一块4个字节的空间来存放了变量 i
int arr[10]={
    
    0};//这是开辟了40个字节来存放数组 arr
//      在这里开辟的内存空间大小是不可变的;
//这里有人想问:我为什么想着改变着的内存空间呢?
//   你想啊,这里我们开辟了一块40个字节的数组,这么大的一块空间可不是想变大就大,
//想小就小的,这可能会和我们所需要的不一样,这会很不方便
//但是如果有一个你想多大就多大的空间呢?
//所以这就是我下来要讲的 动态内存空间管理 了

In the C language, our static memory is opened up, and the characteristics of doing so are:
1.他所开辟的空间是固定的
2.数组在声明的时候,必须指定数组的长度,他所需要的内存在编译的时候分配

But for space requirements, we sometimes don’t know. It is possible that the space is too large to cause waste, and it is also possible that the space is too small to cause stack overflow. In this way, we need a dynamic memory management to let us know how much memory we need. How much to open up.

But it's not that dynamic memory is necessarily good for static memory to open up space, any good thing is a double-edged sword! ! Remember this sentence! ! !


1. Introduction to dynamic memory:

The dynamic memory allocation space is in the heap area, which is different from the static memory allocation. The dynamic memory allocation needs to call the function:

Introduction to functions related to dynamic memory

1. malloc and free

   void*  malloc (size_t  size);        

This function can apply for a continuous space (heap area) in memory, and return the address of the starting position of this space, but it will not initialize this space.

如:
      int* pf= (int *) malloc(40);  //  40 ---->的单位是字节

A space of 40 bytes has been opened up, but you don’t know what type it is, so you need to manually cast the type you need (int*).

Notice:
1. If the allocation is successful, return a pointer to the allocated space.
2. If the allocation fails, a NULL pointer is returned, so the return value of malloc must be checked.
3. The type of the return value is void *, so the malloc function does not know the type of space opened, and the user implements it by himself when using it.
4. If the parameter size is 0, the behavior of malloc is undefined by the standard and depends on the compiler.

这里因为我们有可能会动态内存申请失败的,所以我们应该更加加以检查,养成好习惯,在每次开辟了一块动态内存空间后面立马进行判断是否申请开辟空间成功。

  • C language provides another function free, which is specially used to release and recycle dynamic memory:
  void * free (void * ptr); 

He is specially used to release the memory opened by dynamic memory;

Notice:

1. If the space pointed to by the parameter ptr is not dynamically opened, the behavior of the free function is undefined.
2. If the parameter ptr is a NULL pointer, the function does nothing.

1.当malloc函数申请的内存空间,当程序退出时,会还给操作系统;当程序不退出时,动态内存申请的内存空间,不会主动释放。一定需要free函数来释放,所以我们要养成好习惯,每每开辟一块动态内存空间,后面一定要记得free释放这块空间。
2.free只释放动态内存空间,不能对静态内存操作哦
3.free释放该指针的地址后,会导致该指针为野指针,一定给该指针重新赋值空指针(NULL)

Example:

int main()
{
    
    
	//开辟一块40个字节的动态内存空间,且强转位int*类型
	int* pf =(int *) malloc(40);
	//判断是否开辟成功
	if (pf == NULL)
	{
    
    
		perror("malloc");//如果开辟失败了,打印出原因
		return 1;      //并且不用再继续往下了,就返回1
	}
	//如果开辟成功,则打印出这个地址所指的内容和往后10个的内容
	int i = 0;
	for (i = 0; i < 10; i++)
	{
    
    
		printf("%d\n", *(pf + i));
	}

	//释放开辟的内存
	free(pf);//这里注意,释放pf的地址后,pf就会变成一个野指针,
	//这里一定需要重新给他赋值上NULL,这也是个好习惯
	pf = NULL;

	return 0;
}

insert image description here

This picture also proves that when the dynamic memory opened by malloc, it will not be initialized;

2. calloc function

  • This function is almost similar to the malloc function and is also used for dynamic memory allocation:
   void*  calloc  (size_t  num,size_t  size);
  • The function of the function is to open up a space for num elements whose size is size, and initialize each byte of the space to 0.
  • The only difference from the function malloc is that calloc will initialize each byte of the requested space to all 0s before returning the address.

Example: almost the same code as above;

int main()
{
    
    
	//开辟一块40个字节的动态内存空间,且强转位int*类型
	int* pf = (int*)calloc(10, sizeof(int ));
	//判断是否开辟成功
	if (pf == NULL)
	{
    
    
		perror("calloc");//如果开辟失败了,打印出原因
		return 1;      //并且不用再继续往下了,就返回1
	}
	//如果开辟成功,则打印出这个地址所指的内容和往后10个的内容
	int i = 0;
	for (i = 0; i < 10; i++)
	{
    
    
		printf("%d\n", *(pf + i));
	}

	//释放开辟的内存
	free(pf);//这里注意,释放pf的地址后,pf就会变成一个野指针,
	//这里一定需要重新给他赋值上NULL,这也是个好习惯
	pf = NULL;

	return 0;
}

insert image description here

The same code as above, but the running result is all 0, which proves that the dynamic memory opened by calloc will be initialized to 0.

3.realloc function

  • The realloc function makes dynamic memory management more flexible;
  • Sometimes it is found that the requested space is not suitable, and the memory size needs to be adjusted flexibly, then the realloc function can adjust the dynamically allocated memory size;
 void * realloc ( void* ptr , size_t  size);

Its parameters:
1.ptr is the memory address to be adjusted
2.size is the new size after adjustment
3. The return value is the starting position of the memory after adjustment
4. This function will adjust the size of the original memory space, and also the original memory Data is moved to a new space
5. There are two situations when realloc adjusts the memory space:
————— (1). When adding a piece of space after the original open space: it is found that there is not enough space behind, + diagram:

insert image description here
1.开辟新空间 2.将旧的空间中的数据拷贝到新空间 3.释放旧的空间 4.返回新空间的起始位置地址

—————— (2). If there is enough space in the back, then add the space size of (size-the original byte bytes) in the back. + Diagram:

insert image description here
直接在后面加上一块空间;

If you want to use the realloc function, you first need a piece of dynamic memory space that has been opened, so that you can use realloc to adjust.
accomplish:

int main()
{
    
    
	//开辟一块40个字节的整形空间大小
	int* pc = (int*)malloc(40);
	if (pc == NULL)
	{
    
    
		perror("malloc");
		return 1;
	}
	//初始化 1 - 10 的数字
	int i = 0;
	for (i = 0; i < 10; i++)
	{
    
    
		pc[i] = i + 1;
	}
	//想增加一些空间
	int* ptr = (int*)realloc(pc, 80);
	//     这里我想特别强调一点,为什么不让 pc指针接收,
	//而要创造一个新的指针接收呢?
	//     因为realloc增容可能会失败,那返回的就是NUKLL,
	//而如果把 NULL 给到 pc指针,那会造成内存泄露
	//    当然内存泄露是一个动态内存错误,再这篇博客的后面 
	//会一一介绍到动态内存错误
	//增加空间失败
	if (ptr == NULL)
	{
    
    
		perror("realloc");
		return 1;
	}
	//增加空间成功
	pc = ptr;//是将返回来的地址交给原来的指针维护
	ptr = NULL;

	//打印数据
	for (i = 0; i < 20; i++)
	{
    
    
		printf("%d\n", pc[i]);
	}

	//释放
	free(pc);
	pc = NULL;

	return 0;
}

insert image description here
前面的1到10的初始化,后面是新增加的10个字节大小空间,并没有进行初始化。

- If you want to reduce space, it is even simpler:

The above-mentioned situation does not exist in reducing space, you can directly change the size of the parameter after realloc to what you want.
For example: reduce the original space size to 20 bytes.

int *ptr = (int *) realloc(pc,20);

2. Some common dynamic memory errors:

1. Dereference operation on NULL pointer:

This problem is easy to understand. It should have been mentioned above:
When malloc opens up dynamic memory space, you must remember to check the returned address, because malloc does not necessarily open up space successfully. Will return a null pointer (NULL), and if the pointer needs to be dereferenced later, there will be problems;

int main()
{
    
    
    int *ptr;
    ptr=(int*)malloc(sizeof(int));
    //这里不进行判断,如果时返回NULL则这将会出现问题
    *ptr=1;  
    
	//释放空间
    free(ptr);
    ptr=NULL;
        
    return 0;
}

2. Out-of-bounds access to dynamically allocated spaces

To put it simply, you can only use the space you have opened up. If you cross this space to access other spaces, it is an out-of-bounds access.

#include<stdio.h>
#include<stdlib.h>//malloc 和 free 都在<stdlib.h>的头文件里
int main()
{
    
    
    int *ptr ;
    ptr =(int*)malloc(40); //申请一个动态内存空间为40字节
    if(ptr==NULL) //防止申请空间失败传入了空指针
    {
    
    
        perror("ptr");
        return 1;
    }
 
    for(int i=0;i<20;i++)
    {
    
    
        *(ptr+i)=i;//申请的是四十个字节,这里产生了越界
    }
    
    free(ptr);
    ptr=NULL;
    return 0;
}
 

2.1. Consequences of out-of-bounds access

  • If there are old irons that are not clear, the following is the relatively complete information I found on the Internet, and you can taste it slowly:

1. Undefined behavior: The C language standard stipulates that the compiler will not provide any guarantee for the behavior of out-of-bounds access, and the result is undefined. This means that programs may produce unpredictable results, including crashes, erroneous output, data corruption, etc.

2. Memory corruption: Out-of-bounds access may destroy the memory space of other variables or data structures, causing abnormal behavior or crash of the program.

3. Security vulnerabilities: Some malicious users may use out-of-bounds access vulnerabilities to modify the execution flow of the program, thereby executing malicious code or obtaining unauthorized access rights.

4. Difficulty in debugging: Out-of-bounds access may cause the behavior of the program to become unpredictable, increasing the difficulty of debugging the program.

3. Use free to release non-dynamically allocated memory

#include<stdio.h>
#include<stdlib.h>
int main()
{
    
    
    int *p;
    *p=10;  
    free(p);  //这里的p并不是动态内存空间仍然进行了释放
    return 0;
}

记住一点:非动态无free;

4. Use free to release part of a dynamically allocated memory

#include<stdio.h>
#include<stdlib.h>
int main()
{
    
    
    int *p=(int*)malloc(sizeof(int)*2);
    if(p==NULL)
    {
    
    
        perror("malloc");
        return 1;
    }
    int i=0;
    for(i=0;i<5;i++)
    {
    
        //这里只对前 5个进行赋值
		*p=i;
		p++;//这时p指针也会向高地址走去,
//这代表了p指针不再是指向这块空间的最初的起始位置地址
    }
    //而如果再对这块空间进行释放,还能只释放一部分么?
    //答案是不行的,程序会崩溃
    free(p); 
    p=NULL;
   
}

This kind of error should also contain another error: memory leak;

If instead of the pointer to the beginning of the dynamically allocated memory being freed, a pointer to the middle of the memory block is freed, a memory leak will result. Because the memory space from the pointer position is still allocated, but the program cannot access these memory spaces, resulting in a memory leak.

所有在动态开辟空间时,最好不用动该空间的起始位置,最好重新创建一个指针进行操作,这也是一个好习惯哦;

5. Multiple releases of a piece of dynamic space

This kind of mistake should be due to negligence, thinking that I have not released, but the result is that I have already released (I really cried to death);

#include<stdio.h>
#include<stdlib.h>
int main()
{
    
    
	//开辟
    int *p=(int*)malloc(sizeof(int)*2);
    if(p==NULL)
    {
    
    
        perror("p");
    }
    
   //释放 
    free(p);
   //不记得自己的释放
   //~~~~~~
    free(p);  //再一次释放,会导致不可预测的后果;
    //   对这最好的解决办法就是:当我们释放了一块空间后,
    // 一定让他指为空指针
    p=NULL;
   
}
  • Because if you release a piece of space, you will give a null pointer for maintenance. If you still forget whether you have released it, and release it again, it will not have much impact, because we assign a null pointer in advance to maintain, and free null pointer will do nothing, so it doesn't matter much.

当我们释放了一块空间后,一定让他指为空指针,这是一个好习惯哦;

6. Forgetting to release dynamic memory space (memory leak)

The memory leak was mentioned many times before, and finally here it is

We need to look at a piece of code to understand better:

void test()
{
    
    
    int*p = (int*)malloc(sizeof(int) * 2);
    //这是申请了这块空间返回地址给了指针 p
    if (p != NULL)
    {
    
    
        *p = 20;
    }
    //但是当退出该函数时,指针 p所占的空间会还给操作系统,
    //  这就代表刚刚开辟的空间无法再找到了,
    //     因为只有指针p指向该空间地址
}
int main()
{
    
    
    test(); 
    return 0;
}

If you still don’t understand it, you can help to understand it in this way. I also understand it this way:
If you are an undercover agent, and only one of your one-line bosses, Officer P, knows your undercover identity, but Officer P dies during a mission. Embarrassing, no one knows you anymore, this is equivalent to a memory leak;

切记动态开辟空间一定要记得释放,并且是正确的释放;这是一个好习惯哦。

Learn a little more:

A memory leak is a chronic problem that does not immediately cause a program crash, but gradually occupies system memory, affects system stability and performance, and eventually causes the system to crash.

内存泄漏(Memory Leak)是指程序在运行过程中无法释放已经分配的内存空间,导致系统中存在大量的无用的内存空间,最终可能导致程序崩溃甚至系统崩溃。


end:

It is more important for dynamic memory, because usually: the heap space is larger than the stack space, and we know that dynamic memory can be modified, and we can open up as many bytes as we want. This point can prove his advantages, so when opening up dynamic memory space, we must pay attention to prevent the loss outweighing the gain! ! !

——————————

In the end, it is not easy for children to code and write, thank you for your support, your support is the greatest motivation for children ~~ ^ _ ^ ~~

Guess you like

Origin blog.csdn.net/m0_66780695/article/details/132123639