This article takes you to understand dynamic memory management

Table of contents

The significance of the existence of dynamic memory

Introduction to Dynamic Memory Functions

malloc and free

calloc

realloc

Common Dynamic Memory Errors

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

flexible array 

Features of flexible arrays

Use of flexible arrays

Advantages of flexible arrays


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:

Stack area (stack): When the function is executed, the storage unit of the local variable in the function can be created on the stack, and the function execution end
These storage locations are automatically freed when the program ends. The stack memory allocation operation is built into the instruction set of the processor, which is very efficient, but
The allocated memory capacity is limited. The stack area mainly stores local variables allocated by running functions, function parameters, return data, return
return address etc.

 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.

The data segment (static area) (static) stores global variables and static data. Released by the system after the program ends.
Code segment: store the binary code of the function body (class member functions and global functions).

Explanation for static:
In fact, ordinary local variables are allocated space in the stack area . The feature of the stack area is that the variables created above are destroyed when they go out of scope.
But the variable modified by static is stored in the data segment (static area) , the characteristic of the data segment is the variable created above, until the program
It is destroyed only after the end, so the life cycle becomes longer

flexible array 

In C99, the last element in a structure is allowed to be an array of unknown size, which is called a "flexible array" member.
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;
The above code 1 and code 2 can accomplish the same function, but the implementation of method 1 has two advantages:
The first benefit is: easy memory release
If our code is in a function for others, you do a secondary memory allocation in it and return the entire structure to
user. The user can release the structure by calling free, but the user does not know that the members in this structure also need to be free, so you
Users cannot be expected to discover this. Therefore, if we allocate the memory of the structure and the memory required by its members at one time
, and return a structure pointer to the user, and the user can release all the memory by doing a free once.
The second advantage is: this is conducive to access speed .
Contiguous memory is good for improving access speed and reducing memory fragmentation

Guess you like

Origin blog.csdn.net/paperjie/article/details/131731213