[Advanced C language study notes] 5. Dynamic memory allocation

foreword

Modern computers are basically designed based on the von Neumann architecture. The core of the von Neumann architecture is the "stored program", which stores the program (instruction set) and data in the memory with the same status. But our memory space is not infinite, so in order to make good use of the memory space, the operating system will partition the memory space accordingly, and the memory in different areas has its corresponding function and usage.
insert image description here
For example, local variables and function parameters are usually stored in the stack area. The characteristic of this part of the memory space is that it is temporarily used and released when it is used up (of course, this is done automatically by the operating system and does not require the intervention of the programmer)
; For example, global variables are usually stored in the static area, and local variables modified by static will also be placed in the static area (so the static modification of local variables essentially changes their storage location, from the stack area --> static area), this Part of the memory space has a very long life cycle, which lasts until the end of the entire program;
for example, the constant strings we use will be saved in the constant area. The characteristics of this part of the memory area are similar to "constants" and cannot be modified. Yu added a "const" buff.
(My nonsense: if we can think more and connect different knowledge points when learning, when these contents are strung together, we can form a relatively macro and overall perspective. Going further, it will be better if we can find fun from it ! Now let's get to the point!)


Full text structure:
insert image description here


1. Inquiry

What is dynamic memory allocation/management?

The memory space opened up on the heap area by the programmer to apply to the operating system according to the actual programming needs for the programmer to operate and maintain, the programmer's amusement park! Usually some temporarily used data or variables can be opened at any time and released at any time when they are used up, without having to wait for the operating system to recycle them after the function ends!

Why do you need dynamic memory allocation?

In actual programming, not only a fixed-size memory space, but also a variable-size memory space is often required.
For example, if we want to create an address book to store related information (name, gender, age, phone number, etc.), we know that this complex data should be stored in a structure type and an array of structures. , but the question is "How big should the array be?"
This question is really difficult to answer. If the length is too small, it will cause data overflow and then cause the program to crash.
If it is too large, it will lead to a lot of waste of storage space and low space utilization.
In order to solve this kind of problem, there is dynamic memory allocation, memory space is requested on demand, and as much as you want!

How to create dynamic memory allocation?

It is implemented by the 4 library functions provided by the system, malloc\calloc\realloc\free. We will introduce these four functions in detail later.


Second, the dynamic memory function

Note: The header files of the four functions mentioned below are: stdlib.h

malloc

Function prototype: void * malloc(size_t size);

size_t is unsigned int (unsigned integer)

The function of this function is to allocate a continuous space of size bytes in the dynamic storage area and return a pointer to the space.

1) If the opening is successful, a pointer to the opened space is returned.
2) If the development fails, a NULL pointer is returned, so the return value of malloc must be checked.
3) The type of the return value is void *, so the malloc function does not know the type of the opened space, and the user decides when it is used.
4) If the parameter size is 0, the behavior of malloc is undefined by the standard and depends on the compiler.

How to release and recycle the dynamically opened space?
The C language provides a library function specifically to complete this function --- free


free

Function prototype: void free(void* p)

The function of free is to release the dynamic space pointed to by the pointer variable p, so that this part of the space can be reused.

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


Now look at the actual use:

#include<stdio.h>
#include<stdlib.h>
int main()
{
    
    
	//1.通过动态开辟申请10个int类型的空间
	int* ptr = (int*)malloc(10 * sizeof(int));//通常结合sizeof一起使用
	//根据实际使用强制类型转换为想要的类型
	//2.malloc有可能申请空间失败,所以需要判断一下
	if (ptr == NULL)
	{
    
    
		perror("main");//perror是一个报错函数,实际出错时打印效果为:main:xxxxxx(错误原因)
		return 0;//出错就直接结束函数
	}
	//3.使用 给这10个整型空间赋值
	for (int i = 0; i < 10; i++)
	{
    
    
		*(ptr + i) = i;
	}
	//打印一下
	for (int i = 0; i < 10; i++)
	{
    
    
		printf("%d ", ptr[i]);//这里可以直接使用数组下标的形式,和指针解引用是一样的
	}
	//4.释放
	free(ptr);
	ptr = NULL;//需要手动置为NULL,防止非法访问
	return 0;
}

insert image description here

Note: The content of the space applied for by malloc is a random value. If it is not initialized, some unexpected values ​​may be obtained;

insert image description here

Understanding: If the space pointed to by the parameter ptr is not dynamically allocated, the behavior of the free function is undefined.

insert image description here


Why do we need to release and recycle dynamic memory?

The memory space is limited. If we just blindly apply for space every time we use it, no matter how large the space is, it will be used up, and if the used space is not released, the computer will become more and more stuck, and the program will run more and more. Slower and slower!

Then why manually assign the pointer to NULL (null pointer) after freeing it?

Let's take an example from life. Suppose a boy broke up with his girlfriend. If the boy still keeps the girl's phone number, WeChat, and what's more, the key to the girl's house. If you were the girl, do you want him to still keep these messages and items? You definitely don't want to, right, maybe one day he's unhappy or other reasons to harass you. (That's why there is a saying that when a couple breaks up, don't be disconnected. Of course, if you are this boy, you may still think about reconciliation in the future, and you will never forget it, and there will be echoes~haha).
Closer to home, in programming, if the space pointed to by the pointer has been released, if it is not set to NULL, then it still retains the address of this place, and it is still possible to access this space later, this life is illegal access!

Is there any dynamic allocation function that initializes while applying for space?

The answer is, of course, yes, the calloc to end next is such a function


calloc

Function prototype: 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 O
2) The difference with the function ma1loc is only that calloc will put each byte of the applied space before returning the address. bytes are initialized to all 0s.

For example, in the code just above, if we replace malloc with calloc without manual initialization:

#include<stdio.h>
#include<stdlib.h>
int main()
{
    
    
	int* ptr = (int*)calloc(10, sizeof(int));
	if (ptr == NULL)
	{
    
    
		perror("main");
		//perror是一个报错函数,实际出错时打印效果为:main:xxxxxx(错误原因)
	}
	for (int i = 0; i < 10; i++)
	{
    
    
		printf("%d ", ptr[i]);
	}
	free(ptr);
	ptr = NULL;
	return 0;
}

insert image description here
From the introduction of the above three functions, we may not have a deep understanding of the dynamic manifestation of "dynamic memory allocation". The function to be introduced next is the "soul" of dynamic memory allocation--- realloc


realloc

The emergence of the realloc function makes dynamic memory management more flexible.
Sometimes we find that the space applied for in the past is too small, and sometimes we feel that the space applied for is too large. In order to use the memory reasonably, we must flexibly adjust the size of the memory. The real1lloc function can adjust the size of the dynamically developed memory.

Function prototype: void * realloc(void* ptr, size_t size);

1) ptr is the memory address to be adjusted. size is the new size after adjustment
2) The return value is the starting position of the memory after adjustment
3) On the basis of adjusting the size of the original memory space, this function will also move the data in the original memory to the new space. (This way of moving is actually a copy copy, which will copy the original content to the new memory)
4) There are two situations when realloc adjusts the memory space: Case
1: There is enough space after the original space
2: There is not enough space after the original space

insert image description here

In case 1, to expand the memory, add space directly after the original memory, and the data in the original space does not change.
In case 2, when there is not enough space after the original space, the expansion method is to find another continuous space of suitable size on the heap space to use. This function returns a new memory address.

Due to the above two situations, the use of the realloc function should be paid attention to.

#include<stdio.h>
#include<stdlib.h>
int main()
{
    
    
	int* ptr = (int*)malloc(10 * sizeof(int));
	if (ptr == NULL)
	{
    
    
		perror("main");
		return 0;
	}
	//业务处理
	//进行扩容操作
	int* p = (int*)realloc(ptr, 100 * sizeof(int));
	//注意:不能直接将扩容之后的地址给ptr,因为存在扩容失败的可能,会导致ptr地址丢失
	if (p == NULL)
	{
    
    
		printf("raalloc failed!\n");
		return 0;
	}
	ptr = p;
	//业务处理
	free(ptr);
	ptr = NULL;
	return 0;
}

Three, common dynamic memory errors

1) Dereference operation to null pointer NULL

void test()
{
    
    
	//int* p = NULL;
	int* p = (int*)malloc(INT_MAX);
	*p = 20;//如果p的值为NULL,就会出问题
	free(p);
}

2) Out-of-bounds access to dynamically developed spaces

insert image description here
Out-of-bounds access when I = 10


3) Use free to release non-dynamically allocated memory

insert image description here


4) Use free to release part of a dynamically allocated memory

insert image description here


5) Multiple releases of the same piece of dynamic memory

insert image description here


6) Dynamically open up memory and forget to release (resulting in memory leaks)

#include<stdio.h>
#include<stdlib.h>
void test()
{
    
    
	int* p = (int*)malloc(100);
	if (NULL != p)
	{
    
    
		*p = 20;
	}
}
int main()
{
    
    
	while (1)
	{
    
    
		test();
	}
	return 0;
}

insert image description here


Fourth, flexible array

Maybe you've never heard of the concept of a flexible array, but it does exist. In C99, the last element in a structure is allowed to be an array of unknown size, which is called a "flexible array" member.

In fact, we can also roughly know its meaning from the name. "Flexible" refers to softness and changeability. Flexible already has the meaning of flexibility and changeability.

Example:

struct S
{
    
    
	int n;
	int arr[];//还可以写成这样 int arr[0];
};

Features of flexible array:
1) The flexible array member in the structure must be preceded by at least one other member. (That is to say, members of a flexible array cannot exist alone)
2) The size of the structure returned by sizeof does not include the memory of the flexible array. (When calculating the size, the size of the flexible array members is not considered)

insert image description here

3) The structure containing flexible array members uses the malloc() function to dynamically allocate memory, and the allocated memory should be larger than the size of the structure to accommodate the expected size of the flexible array. (That is, when creating a variable of a structure type containing flexible array members, it needs to be created by dynamic memory development. The reason is: the size of the flexible array is variable, so the size of the structure variable it creates is also variable. , so you need a dynamic way to create it!)

Example:

#include<stdio.h>
struct S
{
    
    
	int n;
	int arr[];//还可以写成这样 int arr[0];
};
int main()
{
    
    
	//struct S s1;这种方式创建的变量无法正常使用
	//printf("%d\n", sizeof(s1));
	//假设我们期望arr的大小是10个int类型
	struct S* ps = (struct S*)malloc(sizeof(struct S) + 10 * sizeof(int));
	return 0;
}

The dynamic development memory distribution is shown in the figure:
insert image description here

Because the space is dynamically created, if the size of the array arr is not enough for subsequent use, it can be dynamically adjusted through realloc, which reflects its "flexible" feature.
insert image description here


Complete code:

#include<stdio.h>
struct S
{
    
    
	int n;
	int arr[];//还可以写成这样 int arr[0];
};
int main()
{
    
    
	//假设我们期望arr的大小是10个int类型
	struct S* ps = (struct S*)malloc(sizeof(struct S) + 10 * sizeof(int));
	ps->n = 10;
	int i = 0;
	for (i = 0; i < 10; i++)
	{
    
    
		ps->arr[i] = i;
	}
	//调整
	struct S* ptr = (struct S*)realloc(ps, sizeof(struct S) + 20 * sizeof(int));
	if (ptr == NULL)
	{
    
    
		perror("main");
		return 1;
	}
	ps = ptr;
	//使用
	//.....
	//释放
	free(ps);
	ps = NULL;
	return 0;
}

Alternative to flexible array functionality Substitute
a pointer for a flexible array member

struct S
{
    
    
	int n;
	//int arr[];//还可以写成这样 int arr[0];
	int* arr;//替换柔性数组
};

Comparing flexible and inflexible arrays

Benefit 1: Aspect memory release
If our code is in a function used by others, you do a secondary memory allocation in it and return the entire structure to the user. The user can free the structure by calling free, but the user doesn't know that the members of the structure also need to be free, so you can't expect the user to find out. 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, the user can release all the memory by doing a free.
Benefit 2: This is good for access speed.
Contiguous memory is good for improving access speed and reducing memory fragmentation. (Actually, I personally don’t think it’s too high. Anyway, you can’t run and use the addition of offsets to address.)
(It involves the memory pool, the principle of locality: the principle of spatial locality, the principle of time locality, interested Students can find information on their own!)


review:
insert image description here

Guess you like

Origin blog.csdn.net/QIYICat/article/details/119079998