C language - dynamic memory management (malloc, free, calloc, realloc)

Table of contents

1. Why does dynamic memory allocation exist?

2. Introduction to dynamic memory functions  

2.1 malloc and free

malloc example:

free example:

 2.2 calloc

realloc example:

3. Common dynamic memory errors

3.1 Dereference operation on NULL pointer

3.2 Out-of-bounds access to dynamically opened space

3.3 Use free release for non-dynamically allocated memory

3.5 Release the same dynamic memory multiple times

3.6 Dynamically allocated memory and forgets to release it (memory leak)

 4.C/c++ memory area division


1. Why does dynamic memory allocation exist?

The memory allocation methods we have mastered are:
int val = 20;//在栈空间上开辟四个字节
char arr[10] = {0};//在栈空间上开辟10个字节的连续空间
However, the above-mentioned way of opening up space has two characteristics:
        ​ ​ 1. The space opening size is fixed.
        ​​​​ 2. When declaring an array, the length of the array must be specified, and the memory it requires is allocated at compile time.
        
        But the demand for space is not just the above situation. Sometimes the amount of space we need can only be known when the program is running, so the method of creating space during compilation of the array is not sufficient. At this time, you can only try dynamic storage development.

2. Introduction to dynamic memory functions  

2.1 mallocfree

The C language provides a function for dynamic memory allocation:
void* malloc (size_t size);
This function applies for a block of continuously available space from the memory, and returns a pointer to this space.
        ​ ​ 1. If the opening is successful, a pointer to the opened space is returned.
        2. If the development fails, a NULL pointer will be returned, so the return value of malloc is certain Have to check.
        3. The return value type is void* , so the malloc function does not know how to open up space The type is decided by the user when using it.
        4. If the parameter size is 0 , malloc The behavior of is undefined by the standard and depends on the compiler. C language provides another function free specifically for For the release and recycling of dynamic memory, the function prototype is as follows:

malloc和freeTokyo statement stdlib.h In the middle of the sentence.

malloc example:

#include <stdio.h>
#include <stdlib.h>

int main()
{
	int* p = (int*)malloc(10 * sizeof(int));
	if (p == NULL)
	{
		perror("malloc");
		return 1;
	}

	return 0;
}

We can see that the memory is successfully opened: 

But if the applied memory is too large, the application will fail (INT_MAX*4 is the maximum value of the integer: 2147483647, including the header file <limits.h>,):

#include <stdio.h>
#include <stdlib.h>
#include <limits.h>

int main()
{
	int* p = (int*)malloc(INT_MAX*4);
	if (p == NULL)
	{
		perror("malloc");
		return 1;
	}

	return 0;
}

At this time p is a null pointer:

The perror function returns an error message, not enough space:

//使用
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		*(p + i) = i;
	}
	for (i = 0; i < 10; i++)
	{
		printf("%d ", p[i]);
	}


 The C language provides another functionfree, which is specially used to release dynamic memory. and recycled, the function prototype is as follows:

void free (void* ptr);

The free function is used to release dynamically allocated memory.

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

free example:

#include <stdio.h>
#include <stdlib.h>
#include <limits.h>

int main()
{
	int* p = (int*)malloc(10 * sizeof(int));
	if (p == NULL)
	{
		perror("malloc");
		return 1;
	}
	//使用
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		*(p + i) = i;
	}
	for (i = 0; i < 10; i++)
	{
		printf("%d ", p[i]);
	}
	return 0;
	free;
	free(p);
	p = NULL;

}

p = NULL; Is it still necessary? Of course it is, please see the picture below: 


 2.2 calloc

The C language also provides a function called calloc , calloc function is also used for dynamic memory allocation. The prototype is as follows:
void* calloc (size_t num, size_t size);
        1. The function of the function is to open up a space for num elements of size size , And initialize each byte of the space to 0 .
        2. The only difference from the function malloc is that calloc will initialize each byte of the requested space to the full value before returning the address. 0 .

calloc example: 

#include <stdio.h>
#include <stdlib.h>
int main()
{
	int* p = (int*)calloc(10, sizeof(int));
	if (NULL != p)
	{
		//使用空间
	}
	free(p);
	p = NULL;
	return 0;
}

        So if we require initialization of the contents of the applied memory space, we can easily use the calloc function to complete the task.  


2.3 realloc
The emergence of the realloc function makes dynamic memory management more flexible.
        Sometimes we find that the space we applied for in the past was too small, and sometimes we feel that the space we applied for is too large. In order to use the memory reasonably, we will definitely be flexible in the size of the memory. adjustment. Then the realloc function can adjust the dynamically allocated memory size.
The function prototype is as follows:
void* realloc (void* ptr, size_t size);

1.ptr is the memory address to be adjusted
2.size is the new size after adjustment
3. The return value is the starting memory after adjustment Location.
4. This function not only adjusts the size of the original memory space, but also moves the data in the original memory to the new space.
5. There are two situations when realloc adjusts the memory space:

        Situation 1: There is a large enough space after the original space

        Situation 2: There is not enough space after the original space

Case 1: When it is case 1, if you want to expand the memory, just add space directly after the original memory, and the data in the original space will not change.
Case 2: When it is case 2 and there is not enough space after the original space, the expansion method is: find another continuous space of appropriate size on the heap space to use. At the same time, the data in the old space is copied to the new space, so that the function returns a new memory address. Due to the above two situations, some attention should be paid to the use of realloc function.

Note: Do not directly give the expanded space to the original pointer. What does this mean?

realloc example:

#include <stdio.h>
#include <stdlib.h>
int main()
{
	int* ptr = (int*)malloc(100);
	if (ptr != NULL)
	{
		//业务处理
	}
	else
	{
		exit(EXIT_FAILURE);
	}
	//扩展容量
	//代码1
	ptr = (int*)realloc(ptr, 1000);//这样可以吗?(如果申请失败会如何?)
	//代码2
	int* p = NULL;
	p = realloc(ptr, 1000);
	if (p != NULL)
	{
		ptr = p;
	}
	//业务处理
	free(ptr);
	return 0;
}

If realloc fails to apply for space, the return value is NULL, and ptr will be directly cleared. Therefore, the correct step should be to hand over a new pointer, and then hand over the original pointer after determining that the expansion has been successful.

Of course, you can also directly use the realloc function to open up space like the malloc function.

int main()
{
	int*p = (int*)realloc(NULL, 40);//== malloc(40);
	if (p == NULL)
	{

	}
	return 0;
}

3. Common dynamic memory errors

3.1 Dereference operation on NULL pointer

Without judging the return value, it is possible to use the NULL pointer and dereference:

int main()
{
	int *p = (int*)malloc(40);
	//不做返回值判断,就可能使用NULL指针,解引用
	*p = 20;

	return 0;
}

Correct way to write:

int main()
{
	int *p = (int*)malloc(40);
	if (p == NULL)
	{

	}
	*p = 20;

	return 0;
}

3.2 Out-of-bounds access to dynamically opened space

void test()
{
	int i = 0;
	int* p = (int*)malloc(10 * sizeof(int));
	if (NULL == p)
	{
		exit(EXIT_FAILURE);
	}
	for (i = 0; i <= 10; i++)
	{
		*(p + i) = i;//当i是10的时候越界访问
	}
	free(p);
}

3.3 Use free release for non-dynamically allocated memory

void test()
{
int a = 10;
int *p = &a;
free(p);//ok?
}

3.4 Use free to release a part of dynamically allocated memory

void test()
{
int *p = (int *)malloc(100);
p++;
free(p);//p不再指向动态内存的起始位置
}

To free up space, you can only release it all.

3.5 Release the same dynamic memory multiple times

void test()
{
	int* p = (int*)malloc(100);
	free(p);
	free(p);//重复释放
}

3.6 Dynamically allocated memory and forgets to release it (memory leak)

void test()
{
	int* p = (int*)malloc(100);
	if (NULL != p)
	{
		*p = 20;
	}
}
int main()
{
	test();
	while (1);
}

Forgetting to release dynamically allocated space that is no longer used can cause memory leaks.
Remember: the dynamically opened space must be released and released correctly.

 4.C/c++ memory area division

Several areas of C/C++ program memory allocation:
1. Stack area (stack): When executing a function, the storage units of local variables within the function can be on the stack Created, these storage units are automatically released when the function execution ends. The stack memory allocation operation is built into the processor's instruction set and is very efficient, but the allocated memory capacity is limited. The stack area mainly stores local variables, function parameters, return data, return addresses, etc. allocated for running functions.
2. Heap area (heap): Generally allocated and released by the programmer. If the programmer does not release it, it may be recycled by the OS when the program ends. The allocation method is similar to a linked list.
3. The data segment (static area) stores global variables and static data. It is released by the system after the program ends.
4. Code segment: stores the binary code of the function body (class member function and global function).

With this picture, we can better understand the example of static keyword modifying local variables mentioned in "First Introduction to C Language".
In fact, ordinary local variables allocate space in the stack area. The characteristic of the stack area is that the variables created above are destroyed when they go out of scope. However, variables modified by static are stored in the data segment (static area). The characteristic of the data segment is that the variables created above are not destroyed until the end of the program, so the life cycle becomes longer.

Guess you like

Origin blog.csdn.net/2301_76618602/article/details/133297595