Detailed Explanation of C Language Dynamic Memory


foreword

Memory is the most error-prone place in our program, especially the dynamic memory in this chapter. The biggest disadvantage of C language is that it needs to do memory management by itself . There is no memory recycling mechanism in other high-level languages. The dynamic memory learned in this chapter requires us to pay attention to memory and avoid memory leaks.

First, why there is dynamic memory allocation

Among the memory development methods we have mastered are variables and arrays:

	int num = 10;//在栈空间上开辟4个字节
	int sum[10] = {
    
     0 };//在栈空间上开辟40个连续的字节

In the above open space, the size of the space open is fixed or the array length must be specified when declaring the array so that the space can be allocated at compile time . But sometimes we don’t know the size of the space we need, such as how many students there are in a class, whether there will be new students joining the class later, the space opened up by static arrays is very limited, which will cause space waste or insufficient space. This time requires dynamic memory allocation.
Let's first understand the memory distribution through a picture:
insert image description here
the variables we created before are generally located on the stack, and the dynamically opened space is located in the heap area. The stack can increase downwards, and the heap can increase upwards.
1. Stack area (stack): When the function is executed, the storage unit of the local variables in the function can be created on the stack, and it will be released automatically when the function ends. The stack area mainly stores the local variables allocated by the function running, function parameters, and return data , return address, etc.
2. Heap area (heap): Generally allocated and released by the programmer, if the programmer does not release, it may be recycled by the OS at the end of the program.
3. Code segment: store the binary code of the function body.

2. Dynamic memory allocation

The function of dynamic memory allocation should include the header file stdlib.h.

1.malloc

Function prototype:

void* malloc(size_t size);

This function can apply for a continuous available space from the memory and return a pointer to this space .
Note:
If the allocation is successful , a pointer to this space will be returned.
If the development fails , a NULL pointer is returned. The return value of malloc must be checked .
If size is 0, it is undefined behavior and depends on the compiler.
The return value of the function is void* type, which we can convert to the type we use as needed .
The code is as follows (example):

int main()
{
    
    
	//向内存申请10个连续的整形空间
	int* p = (int*)malloc(10 * sizeof(int));
	if (p == NULL)
	{
    
    
		printf("%s\n", strerror(errno));
		exit(1);
	}
	else
	{
    
    
		int i = 0;
		//为开辟的空间赋值
		for (i = 0; i < 10; i++)
		{
    
    
			*(p + i) = i;
		}
		for (i = 0; i < 10; i++)
		{
    
    
			printf("%d ", *(p + i));
		}
	}
	free(p);
	p = NULL;
	return 0;
}

insert image description here

2.free

Function prototype:

void* free(void* ptr);

The free function is used to release dynamically allocated memory.
If the space pointed to by the parameter ptr is not dynamically opened, the behavior of the free function is undefined .
If the parameter ptr is a NULL pointer, the function does nothing. .

3.calloc

Function prototype:

void* calloc(size_t num,size_t size);

The function of this function is to open up a space for num elements of size size, and initialize each byte of the space to 0 .
Compared with malloc, this function will initialize the requested space before returning the address .
The code is as follows (example):

int main()
{
    
    
	int* p = (int*)calloc(10, sizeof(int));
	if (p == NULL)
	{
    
    
		printf("%s\n", strerror(errno));
		exit(1);
	}
	else
	{
    
    
		int i = 0;
		for (i = 0; i < 10; i++)
		{
    
    
			printf("%d ", *(p + i));
		}
	}
	free(p);
	p = NULL;
	return 0;
}

insert image description here

4.realloc

The realloc function can make dynamic memory management more flexible.
When our application space is too small or too large, in order to use memory reasonably, we can use realloc to adjust the size.

void* realloc(void *ptr,size_t size);

ptr is the memory address to adjust .
size is the adjusted size .
The return value of the function is the adjusted memory starting position .
There are two situations in which realloc adjusts the memory space:
1. When there is enough space after the original space, add space directly after ptr, and then return to ptr.
2. If there is not enough space behind the original space. Then find a new space. And copy the data in the original memory to the new space, automatically release the old space and return the address of the new space .
The code is as follows (example):

int main()
{
    
    
	int* p = (int*)malloc(5 * sizeof(int));
	if (p == NULL)
	{
    
    
		printf("%s\n", strerror(errno));
		exit(1);
	}
	else
	{
    
    
		int* ptr = realloc(p, 10 * sizeof(int));
		if (ptr != NULL)
		{
    
    
			p = ptr;
		}
		int i = 0;
		for (i = 0; i < 10; i++)
		{
    
    
			*(p + i) = i;
		}
		for (i = 0; i < 10; i++)
		{
    
    
			printf("%d ", *(p + i));
		}
	}
	free(p);
	p = NULL;
	return 0;
}

insert image description here
Note:
realloc needs to use the new pointer to receive first. If realloc fails and the original pointer is used to receive, the original pointer will be lost.

3. Common memory errors

1. Dereferencing the NULL pointer: This situation generally exists when the dynamically allocated memory is directly used without judging whether it is successfully opened.
2. Out-of-bounds access to dynamically allocated space: This situation generally exists when the allocated space is less than the space to be accessed.
3. Use free to release the memory of the non-dynamically opened space
4. Use free to release part of the dynamically opened memory: This situation generally exists when the pointer of the opened space is changed, so that the pointer is not pointing to the starting position, and the free release is performed part will be released.
5. Multiple releases of the same piece of dynamic memory: This situation commonly exists in multiple releases of dynamic memory. The solution is to set the pointer of the released memory to NULL. Even if there are multiple releases of the modified memory, it will not cause problems.

4. Flexible array

The flexible array exists in the structure.
The characteristics of the flexible array:
1. There must be at least one other member before the flexible array member of the structure.
2. The size of this structure returned by sizeof does not include the memory of the flexible array.
3. The structure containing the flexible array uses malloc for memory allocation, and the allocated memory is larger than the size of the structure to accommodate the expected size of the flexible array.
The code is as follows (example):

struct S
{
    
    
	int n;
	int arr[0];
};
int main()
{
    
    
	struct S* p = (struct S*)malloc(sizeof(struct S) + 5 * sizeof(int));
	p->n = 10;
	int i = 0;
	for (i = 0; i < 5; i++)
	{
    
    
		p->arr[i] = i;
	}
	for (i = 0; i < 5; i++)
	{
    
    
		printf("%d ", p->arr[i]);
	}
	free(p);
	p=NULL;
	return 0;
}

insert image description here

struct S
{
    
    
	int n;
	int* arr;
};
int main()
{
    
    
	struct S* p = (struct S*)malloc(sizeof(struct S));
	p->arr = malloc(5*sizeof(int));
	int i = 0;
	for (i = 0; i < 5; i++)
	{
    
    
		p->arr[i] = i;
	}
	for (i = 0; i < 5; i++)
	{
    
    
		printf("%d ", p->arr[i]);
	}
	free(p->arr);
	p->arr = NULL;
	free(p);
	p = NULL;
	return 0;
}

insert image description here
One of the above forms can also achieve dynamic development, why set up a flexible array?
The first advantage of using a flexible array is to facilitate memory release, which can be compared with the above two codes. The second advantage is that it is beneficial to access speed. Memory contiguousness is conducive to improving access speed and reducing memory fragmentation.

Summarize

Dynamic memory allocation greatly improves the flexibility of our programming, but most of the errors in C language also come from this. Dynamic memory allocation requires us to pay attention to many things to reduce memory errors.

Guess you like

Origin blog.csdn.net/2301_76986069/article/details/130744605