Dynamic memory management and usage summary [nanny benefits for beginners]

Dynamic memory management and application summary
A blog to learn the management and use of dynamic memory
This blog is full of dry goods. It is recommended to collect and read it again!!
Please, please, please, please, please, thank you 🙏

It's not easy to like first and then look good. It's all done with great care. I hope to get support. Your praise and support is a very important motivation for me. Don't forget to pay attention to me after reading it! ️️️
This article basically covers all the knowledge points of dynamic memory in the initial learning stage. Your favorite partners must read it

Key points of this chapter
Introduction to dynamic memory functions
Common errors in dynamic memory and their avoidance
Flexible array

Introduction to dynamic memory functions

To learn dynamic memory allocation, we must know how dynamic memory is developed and released.

malloc() and free()

The prototype of malloc() void* malloc (size_t size);
Using malloc function, we can directly apply for the space we want on the heap.
Note that the space created with malloc() is on the heap of memory, not on the stack
If malloc() successfully opens up a space, it will return the address of the newly opened space. If it fails, it will return a null pointer.
Every time you use the space opened on the heap, you must remember to release the space just now and return it to the computer. The free() function is used.

int main()
{
	int*p=(int*)malloc(10 * sizeof(int));//Open up space in reactor area 
	//However, void * cannot be dereferenced
	//Before using these spaces, be sure to check whether malloc() successfully opens up space
	if (p == NULL)
	{
		perror("main");
		return 1;//End function
	}
	//use
	//...
	//Recycle space
	free(p);
	//But does P not automatically set p to NULL?
	//The answer is no, in order to prevent illegal access in the future
	p = NULL;//Set p to NULL by yourself
	return 0;
}

This is a standard template for opening up space with malloc() and releasing space with free() after use
Here are some points to note.
1. After using malloc() heap space, you must remember to check whether the development fails, otherwise the null pointer may be dereferenced.
2. After using the space on the heap, be sure to use the free() function to release the space and return it to the memory.
Note: free() can only release the dynamically opened space, that is, the space on the heap, and cannot be used to release the space on the stack

int main()
{
	int a = 10;
	free(&a);//error
	return 0;
}

To sum up, malloc() and free() should be used in pairs

calloc()

Let's first look at the function prototype: void* calloc (size_t num, size_t size);
We can see it at www.cplusplus.com
This function is used to allocate and zero initialize array, that is, open an array initialized to 0 on the heap
The two parameters are the number of elements you want to open up and the size of each element

int main()
{
	int* p = (int*)calloc(10,sizeof(int));
	if (p == NULL)//Check whether the development is successful
	{
		perror("main");
		return 1;
	}
//Use process
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		*(p + i)=5;
	}
	//Recycle space
	free(p);
	p=NULL;
	return 0;
}

Unlike malloc(), the space opened by the calloc() function has been initialized to 0, while the space opened by malloc() is not initialized. We can write a small piece of code ourselves. After f10 runs, open the memory window and you can see it

realloc()

The realloc function Changes the size of the memory block pointed to by ptr
Let's first look at the prototype of realloc function: void* realloc (void* ptr, size_t size);
The first parameter is the pointer to a piece of dynamic memory, and the second parameter is the new size of the space you want to set.
Based on the above code, let's explain our realloc() function

int main()
{
	//int* p = (int*)malloc(40);// Not initialized yet
	int* p = (int*)calloc(10,sizeof(int));//Initialized
	if (p == NULL)
	{
		perror("main");
		return 1;
	}
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		*(p + i)=5;
	}
	//I want ten more integer addresses
	//realloc to adjust the space
	int*ptr=(int*)realloc(p, 20 * sizeof(int));
	//Additional space
	//Judge whether the added space is successfully added
	if (ptr != NULL)
	{
		p = ptr;
	}
    //use
	for (i = 0; i < 10; i++)
	{
		printf("%d ", *(p + i));
	}
	//Free up space
	free(p);
	p = NULL;
	return 0;
}

However, the use and understanding of realloc() we need to master is not so simple
When using realloc() to add space, the following situations often occur

1.If realloc()Discover the original p There is enough space behind the pointed space - directly increase the capacity and return the original address
2.If realloc()find p When there is not enough space behind the pointing space,
  It will find a new space,
  And copy the original content and put it in the newly found space,
  And release the old address.
  3.If no suitable space is found in the whole heap, a null pointer is returned.
Therefore, it should be noted that:
	Generally, you can't use the original one directly p To accept realloc()Return value of,
	In case there is no space in the pile, return the original p It's gone
	If so, we will never find the space we opened up at the beginning
	Therefore, a temporary pointer is usually created int*ptr To receive realloc()Return value of function
	And judge the success of the development, and then ptr Endow p Just.

Common errors in dynamic memory usage and their avoidance

My friends who have followed me know that I have already posted a dynamic memory lightning protection article before publishing this blog, which is the same as the following content. My friends can go to my other blog to understand this part of the content, or continue to read it here

Illegal dereference of null pointer

Generally speaking, we don't deliberately dereference null pointers with a certain foundation. However, when we use dynamic memory development, we may dereference "invisible" null pointers

int main()
{
	int*p=(int*)malloc(10000000000000000000);
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		*(p + i) = i;//err
	}
	return 0;
}

In the above code, there seems to be no problem. However, we know that malloc function may fail to open up dynamic memory space. We open up so many bytes of space at one go. If we don't have enough space on the heap, malloc function will fail to open up. We can know that if the function fails to open up, we can search www.cplusplus.com, It will return a null pointer. At this time, our p is a null pointer. How can a null pointer be dereferenced?
We can use a perror() function before for to view our problem, perror("main:");


In order to avoid this problem when writing programs, we can use the pointer after asserting with assert(), or use the if statement to use the pointer when the pointer is true

Cross border visits

int main()
{
	int* p = (int*)malloc(10 * sizeof(int));
	if (p == NULL)
	{
		return 1;
	}
	int i = 0;
	//For cross-border access, we only opened up 40 bytes, not 40 integers
	for (i = 0; i < 40; i++)
	{
		*(p + i) = i;
	}
	free(p);
	p = NULL;
	return 0;
}

Just like this code, we must know that the unit of malloc function when it is opened on the heap is bytes, but here we have to access 40 integers, that is, 160 bytes, which is out of bounds
We can avoid this problem by thinking clearly every step of writing.

Use free to free up non dynamic space

free can only be used to release the dynamically opened space on the heap, but not the space on the stack. The memory space on the stack will be automatically released at the end of the function and the end of the program.

//3. Use free to release non dynamic space
int main()
{
	int arr[10] = { 0 };
	int* p = arr;
	free(p);//err
	p = NULL;
	return 0;
}

We can avoid this problem by thinking clearly every step of writing.

Use free to free a portion of dynamic memory

Released, but not completely released

//4. Use free to release part of dynamic memory
int main()
{
	int* p = malloc(10 * sizeof(int));
	if (p == NULL)
	{
		return 1;
	}
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		*p++ = i;//My p is gone
	}
	free(p);//err
	p = NULL;
	return 0;
}

*In this code, after the dynamic memory pointed by the p pointer is opened up, our p changes its own value in subsequent operations. What does this mean?
This means that nothing can find this space again. At that time, free § is just free() missing some space behind p. what about the space in front of p? Forget it?
So if p this position changes, it's a terrible thing not to find it.

So how can we avoid it?
If we want to operate p in the future, we will use another pointer variable to record the position of my first p at the beginning, and finally free() this pointer variable to record the starting position, so as to avoid the problem of "free(), but not completely free()"

The problem of multiple releases of the same dynamically opened space

//5. Release the same dynamic space for many times
int main()
{
	int* p = (int*)malloc(100);
	//use
	//...
	free(p);//If one is not found in the function, this will happen

	//
	free(p);
	return 0;
	//So at this time, we need to finish free again and immediately set p as a null pointer
	//In this way, when you call free() the second time, pass the null pointer, and nothing will happen
}

See here, some partners may ask: I certainly won't write two free together
In fact, it's just obvious here. If the function has been free, a lot of code has been written later, and finally think of free again, this problem will occur
In order to avoid this problem, we must actively set our p to NULL immediately after free(): p=NULL; In this way, even if the free() function passes NULL to it later, there will be no problem.

Forget to release the dynamically opened space (a more serious problem)

//6. Forget to release the dynamically opened space
void test()
{
	int* p = (int*)malloc(100);
	if (p == NULL)
	{
		return;
	}
	//use
	//Forgot to release
	// 
	// 
	// 
	//When we work out the function, p will be released automatically, so no one can find the space on the heap
	//If you return, no one can find the space pointed to by p
	//This can cause memory leaks
}
int main()
{
	test();
	return 0;
}

When we introduced the problem of freeing in the previous section, we already know that not freeing () will cause memory leakage
Here, I'd like to summarize the two ways to release the dynamically opened space on the heap
1. The programmer releases the code actively, that is, calls the free() function
2. End of procedure
At the beginning of using dynamic memory, this problem may not seem so serious. The program seems to run as usual. Anyway, it doesn't matter if it leaks. It will be released automatically after the program ends
But if this is a server program and runs 24 hours, if the memory is only 32 g and leaks a little every day, isn't the machine getting more and more stuck?, Finally, it will only get stuck. If it gets stuck, it will restart. If it gets stuck, it will restart again. This problem will cause great trouble to the use of our machines. So memory leakage is actually a serious problem, so we must remember free()

Flexible array

Although the concept of flexible array is not common in our use, it is also one of the key points we should master
In C99, the last element in the structure is allowed to be an array of unknown size, which is called a flexible array member

struct S
{
	int n;
	int arr[];//The size is unknown
	//int arr[0]; it's fine too
};

The arr [] here is a flexible array with unknown size.
How do we use it

struct S
{
	int n;
	int arr[];//The size is unknown
	//int arr[0]; it's fine too
};
int main()
{
	//The expected size of arr is 10 integers
	struct S*ps=(struct S*)malloc(sizeof(struct S) + 10 * sizeof(int));
	ps->n = 10;
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		ps->arr[i] = i;
	}
	//Contains a flexible array structure, unified with malloc to open up space
	//increase
	struct S*ptr=realloc(ps, sizeof(struct S) + 20 * sizeof(int));
	//Increased to 20
	if (ptr != NULL)
	{
		ps = ptr;
	}
	//use
	//......
	//release
	free(ps);
	ps = NULL;
	return 0;
}

Points to note: 1. For structures containing flexible arrays, malloc() is used to open up space. 2. Before opening up space, we should have the expected size of a flexible array, that is, how much we want to use. We should be clear

Analog implementation of flexible array

We know that when we define arr [] inside the structure, we don't need to define the array size, and at the same time, the array name is the address of the first element, so why don't we put int arr []; Directly written as int*arr;?
Using this definition method, arr is no longer a flexible array. However, in order to simulate the function of flexible array, we can open up the space of arr on the heap

struct S
{
	int n;
	int* arr;
};
//We need to make sure that the space pointed to by our n and arr is opened up on the heap
int main()
{
	struct S*ps=(struct S*)malloc(sizeof(struct S));
	//First open up a piece for struct S
	if (ps == NULL)
	{
		return 1;
	}
	//Find another pointer to arr
	//The pointer ps just now points to the whole structure S
	//Now make a pointer to arr
	//With this pointer arr, we can manipulate the array
	ps->arr =(int*)malloc(10 * sizeof(int));
	if (ps->arr == NULL)
	{
		return 1;
	}


    //use
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		ps->arr[i] = i;
	}
	//increase
	int*ptr=realloc(ps->arr, 20 * sizeof(int));
	if (ptr != NULL)
	{
		ps->arr = ptr;
	}
	//When releasing, you must release arr first and then ps
	//If ps is released first, arr cannot be found
	free(ps->arr);
	ps->arr = NULL;
	free(ps);
	ps = NULL;
	return 0;
}

To realize the function of flexible array, you must open up space for arr on the heap, not on the stack. 2. 2. We should use the pointer ps - > arr to operate the array. 3. When releasing space, we must release arr first and then ps. Why? Because once ps is released, nothing can find arr, which will cause memory leakage
The first is the use of flexible arrays
The second is not. It's just a way to use pointers to realize the function equivalent to a flexible array
However, in contrast, the first method is better to use flexible arrays
Because the second method has two malloc() and two free(), it is easier to make mistakes
If memory blocks are used frequently, efficiency will be reduced
Multiple malloc() is not a good thing

Epilogue

If you can see the friends here, if you have fully understood the content of this blog, I believe your understanding of dynamic memory has been improved to a higher level. If you feel this blog is helpful to you, don't forget your praise, collection and attention

Tags: C C++

Posted on Sat, 06 Nov 2021 08:37:15 -0400 by israely88