Table of contents
The significance of the existence of dynamic memory
Introduction to Dynamic Memory Functions
Dereference operation on NULL pointer
Out-of-bounds access to dynamically allocated space
Use free to release non-dynamically allocated memory
Use free to release part of a dynamically allocated memory
Free the same block of memory multiple times
Dynamically allocate memory and forget to release
classic written test questions
Memory allocation of C/C++ program
The significance of the existence of dynamic memory
int a = 10;
int arr[10] = { 0 };
The space opened up by the above variables has two characteristics:
the size of the space opened up is fixed
When the array is declared, the length of the array must be specified, and the memory it needs is allocated at compile time
However, sometimes we only know the size of the space when the program is running, and the compilation of the array is the way to open up the space, which cannot be satisfied. At this time, dynamic memory development is required.
Introduction to Dynamic Memory Functions
malloc and free
void* malloc (size_t size)
The malloc function applies for a continuous available space from the memory and returns a pointer to this space.
Note:
If the allocation is successful, a pointer to this space will be returned. ,
If the opening fails, a null pointer is returned, and the return value of malloc is used to check
The type of the return value is void* type, malloc does not know the type of space opened up, it needs to be decided by the user.
If size is 0, the behavior of malloc is undefined by the standard
C language also provides another function free, which is used to release dynamic memory.
void free (void* ptr)
The free function is used to release dynamically allocated memory
If the parameter ptr points to a space that is not dynamically allocated, this behavior is undefined by the standard.
If the parameter ptr is a NULL pointer, the function will do nothing
The declarations of malloc and free are in stdlib.h
Take a chestnut:
Be careful to change ptr to NULL, otherwise it will be a wild pointer
#include <stdio.h>
#include <stdlib.h>
int main()
{
int arr[10] = { 0 };
int* ptr = (int*)malloc(40);
if (ptr == NULL)
return 1;
for (int i = 0; i < 20; i++)
{
*(ptr + i) = i;
}
free(ptr); //释放空间
ptr = NULL;//把ptr变为NULL,不然他是野指针
return 0;
}
calloc
void* calloc (size_t num, size_t size)
calloc initializes num elements of size size to 0
The difference with malloc is that calloc will initialize all the requested space to 0 before returning the address
int main()
{
int* ptr = (int*)calloc(10, sizeof(int));
if (ptr == NULL)
return 1;
for (int i = 0; i < 10; i++)
{
printf("%d\n",*(ptr + i));
}
free(ptr);
ptr = NULL;
return 0;
}
realloc
void* realloc (void* ptr, size_t size)
The emergence of realloc can make dynamic memory management more flexible. Sometimes we find that the space we applied for in the past is too small, and sometimes we feel that it is too big. In order to manage memory reasonably, we must adjust the size of memory. realloc can be done to adjust the size of dynamically allocated memory.
ptr is to adjust the memory address
size is the adjusted size
The return value is the adjusted memory starting position
On the basis of adjusting the size of the original memory space, this function will move the data in the original space to the new space
There are two situations when realloc adjusts the original memory space:
1 There is enough space after the original space
2 There is not enough space after the original space
Situation 1: When expanding the memory, directly add space behind the original memory, and the data in the original space will not change.
Case 2: When there is not enough space after the original space, another space will be found on the heap to use, and the function returns this new memory address.
chestnut:
int main()
{
int* p = (int*)malloc(40);
if (p == NULL)
return 1;
for (int i = 0; i < 10; i++)
{
*(p + i) = i;
}
int* ptr = (int*)realloc(p, 50);
if (ptr == NULL)
return 1;
p = ptr;
ptr = NULL;
for (int i = 0; i < 20; i++)
{
printf("%d ", *(p + i));
}
free(p);
p = NULL;
return 0;
}
Common Dynamic Memory Errors
Dereference operation on NULL pointer
void test()
{
int *p = (int *)malloc(INT_MAX/4);
*p = 20;//如果p的值是NULL,就会有问题
free(p);
}
There is no judgment on p here. If malloc fails to apply for space, it will be a null pointer, and problems will arise when dereferencing the null pointer.
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);
}
Observing the code, we found that when i is equal to 10, access will be out of bounds, resulting in uncontrollable factors.
Use free to release non-dynamically allocated memory
void test()
{
int a = 10;
int *p = &a;
free(p);
}
Use free to release part of a dynamically allocated memory
void test()
{
int *p = (int *)malloc(100);
p++;
free(p);//p不再指向动态内存的起始位置
}
Freeing after the p pointer is moved will cause part of the memory to be unable to be released
Free the same block of memory multiple times
void test()
{
int *p = (int *)malloc(100);
free(p);
free(p);//重复释放
}
Dynamically allocate memory and forget to release
void test()
{
int *p = (int *)malloc(100);
if(NULL != p)
{
*p = 20;
}
}
int main()
{
test();
while(1);
}
Forgetting to release the dynamically allocated memory will cause a memory leak and make this part of the memory unusable
classic written test questions
void GetMemory(char *p)
{
p = (char *)malloc(100);
}
void Test(void)
{
char *str = NULL;
GetMemory(str);
strcpy(str, "hello world");
printf(str);
}
The str in this code is called by value. This function makes a new copy of str. The function accepts this function and destroys it. If str is still NULL, strcpy cannot be used, and printf cannot print
char *GetMemory(void)
{
char p[] = "hello world";
return p;
}
void Test(void)
{
char *str = NULL;
str = GetMemory();
printf(str);
}
The p returned here is a wild pointer, because the space opened by the function p is destroyed, the content pointed to by p is unknown, and the printed content is also unknown.
void GetMemory(char **p, int num)
{
*p = (char *)malloc(num);
}
void Test(void)
{
char *str = NULL;
GetMemory(&str, 100);
strcpy(str, "hello");
printf(str);
}
Although this is a pass-by-address call, it did not release the allocated space in the end, resulting in a memory leak.
void Test(void)
{
char *str = (char *) malloc(100);
strcpy(str, "hello");
free(str);
if(str != NULL)
{
strcpy(str, "world");
printf(str);
}
}
Here is to release the space in advance, so that there is no place for them to store the addresses copied later
Memory allocation of C/C++ program
Areas of memory allocation:
Heap area (heap): Generally allocated and released by the programmer, if the programmer does not release it, it may be recovered by the OS at the end of the program.
flexible array
struct st_type
{
int i;
int a[0];//柔性数组成员
};
struct st_type
{
int i;
int a[];//柔性数组成员
};
Some compilers can use the first method, some can use the second
Features of flexible arrays
A flexible member in a structure must be preceded by at least one other member
sizeof finds the size of this structure will not include flexible arrays
When using malloc for dynamic memory allocation of a structure including flexible array members, the allocated memory should be larger than the size of the structure in order to accommodate the expected size of the flexible array
typedef struct st_type
{
int i;
int a[0];//柔性数组成员
}type_a;
printf("%d\n", sizeof(type_a));//输出的是4
Use of flexible arrays
typedef struct st_type
{
int i;
int a[0];//柔性数组成员
}type_a;
int i = 0;
type_a *p = (type_a*)malloc(sizeof(type_a)+100*sizeof(int));
//业务处理
p->i = 100;
for(i=0; i<100; i++)
{
p->a[i] = i;
}
free(p);
This flexible array is equivalent to obtaining a continuous space of 100 integers
Advantages of flexible arrays
The above code can actually be designed like this:
typedef struct st_type
{
int i;
int* p_a;
}type_a;
type_a* p = (type_a*)malloc(sizeof(type_a));
p->i = 100;
p->p_a = (int*)malloc(p->i * sizeof(int));
//业务处理
for (i = 0; i < 100; i++)
{
p->p_a[i] = i;
}
//释放空间
free(p->p_a);
p->p_a = NULL;
free(p);
p = NULL;