About dynamic memory allocation
Recall the memory allocation methods we learned before:
int val = 20;//在栈空间上开辟四个字节
char arr[10] = {
0};//在栈空间上开辟10个字节的连续空间
When learning C language, we know that data structures are usually fixed size. Take the array as an example. Once the program is compiled, the size of the array and the number of elements are determined . Then the size of the data structure cannot be changed without modifying the program and compiling the program again. The summary is the following two characteristics:
- The space opening size is fixed.
- When declaring an array, the length of the array must be specified. Once the size of the array space is determined, it cannot be adjusted.
But the demand for space is not just the above-mentioned situations. 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.
So the C language introduced dynamic memory allocation, which allows programmers to apply for and release space themselves . The following will introduce how to dynamically open memory.
Introduction to malloc and calloc functions
The following is the definition of cplusplus pair malloc
:
void* malloc (size_t size);
This function applies for a continuous space in memory and returns a pointer to this space. size
That is, the size of the memory space you want to apply void*
for is the address of the first element of the requested memory. Because you don’t know the type, you use it void*
. There are also the following points to note:
- If the allocation is successful, a pointer to the allocated space is returned.
- If the allocation fails, a NULL pointer will be returned, so the return value of malloc must be checked.
- The type of the return value is void*, so the malloc function does not know the type of space to be opened. The user must decide by himself when using it.
- If the size parameter is 0, the behavior of malloc is undefined by the standard and depends on the compiler.
Let’s take a look at the definition of cplusplus pair calloc
:
void* calloc (size_t num, size_t size);
malloc
In fact, calloc
it is very similar to. ralloc
The parameter in size
is the size of each data type that you want to apply for, which num
is the number of data types that you want to apply for. The total size of the application is num*size
, in fact, it can be represented malloc
by size
. The other features are malloc
similar, so I won’t introduce them in detail. Of course, there are differences
between the two , as follows:
malloc
The only difference from the function is that each bytecalloc
of the requested space is initialized to 0 before returning the address .
So if we want to initialize the dynamically applied memory space to 0, it is ralloc
more convenient to use.
Dynamic memory recycling----free
In fact malloc
, calloc
functions such as dynamically allocating memory actually allocate memory on the heap area. Since the memory space requested by these functions is not automatically reclaimed by the system , using such functions too frequently to open up space will lead to heap exhaustion. At this time, we need to actively release the opened space , so we introduce free
a function. The function prototype is as follows:
void free (void* ptr);
The pointer here ptr
points to the first address of our dynamically allocated memory . Only by pointing to the first address can the dynamically allocated memory space be completely released. There are two special cases regarding ptr
pointers ;
- If
ptr
the space pointed to by the parameter is not dynamically allocated,free
the behavior of the function is undefined.- If the argument
ptr
isNULL
a pointer, the function does nothing.
Two more things to note :
- After we release the opened space, the original pointer pointing to this space
ptr
still stores the address here. In order to avoid accidentally assigning or dereferencing this pointer later, causing a wild pointer problem , after releasing the space, we also This pointer needs to be assignedNULL
. - When writing code, it is best to always have a pointer to this space. If there is no pointer to this space, then this space will not be accessible and freed. For programs, inaccessible space is also called garbage, and programs that leave garbage have memory leaks .
As shown in the figure above, the original pointer p
points to the first memory block, and after the operation, p
it points to the second memory block. So since there is no pointer pointing to the first memory block, this memory block can no longer be used. This is the garbage mentioned above, causing a memory leak .
realloc function introduction
Sometimes we find that the space we applied for in the past is too small, and sometimes we feel that the space we applied for is too large. In order to apply for memory reasonably, we must make flexible adjustments to the memory size. At this time, realloc
the function can adjust the dynamically allocated memory size .
The function prototype is as follows:
void* realloc (void* ptr, size_t size);
The pointer ptr
points to the memory address to be adjusted size
and the adjusted memory size. The return value is the starting address of the adjusted memory.
Case 1: There is enough space after the original space
. When it is case 1, if you want to expand the memory, just add space directly after the original memory. The data in the original space will not change.
Case 2: There is not enough space after the original space
. 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. . In this way, the function returns a new memory address. In this case, for the data already in the original memory, this function will copy those data to the new memory , and the original memory will be released .
There is also the problem of not being able to find the original memory space realloc
due to the pointer being assigned when the dynamic memory space fails to be opened . NULL
We generally create a new pointer to receive the address , and NULL
then assign it to the original pointer after judging that it is not correct, as follows:
int main()
{
int* ptr=(int*)malloc(5*sizeof(int));
int* p=(int*)realloc(10*sizeof(int));
if(p != NULL)
ptr = p;
//......(代码)
free(ptr);
ptr = NULL;
return 0;
}
Common dynamic memory errors
- Dereference operation on NULL pointer:
void test()
{
int *p = (int *)malloc(INT_MAX/4);
*p = 20;
free(p);
}
If the value in this code p
is NULL
, there will be a problem.
- Out-of-bounds access to dynamically allocated 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);
}
- Use free release for non-dynamically allocated memory:
void test()
{
int a = 10;
int *p = &a;
free(p);
}
The one here a
is opened in the stack area. If you use free
the release system, an error will be reported.
- Release the same dynamic memory multiple times:
void test()
{
int *p = (int *)malloc(100);
free(p);
free(p);//重复释放
}
malloc
The dynamic memory space opened here is released repeatedly, and the system will also report an error.
- Use free to release a part of dynamically allocated memory:
void test()
{
int *p = (int *)malloc(100);
p++;
free(p);
}
Because ++
the symbol will change the value of the variable, it p
no longer points to the starting position of the dynamic memory. At this time, using free
release will not release all the dynamic memory.
- Dynamically allocated memory and forget to release it (memory leak):
void test()
{
int *p = (int *)malloc(100);
if(NULL != p)
{
*p = 20;
}
}
int main()
{
test();
while(1);
}
After calling test()
the function, the opened memory space is not actively released. The same memory space in the stack area will be recycled after calling this function, so the opened space int* p
cannot be found . This is the garbage mentioned above, and the garbage left malloc
The program has a memory leak . So remember that dynamically opened memory must be released!