Computer common sense 6000 word summary dynamic memory management

Computer common sense 6000 word summary dynamic memory management

catalogue

1, Memory partition

2, Four functions

①malloc

②free

③calloc

④realloc

3, Error prone analysis

Question 1:

Question 2:  

Question 3:

Question 4:

Question 5:  

Question 6:

4, Classic interview questions

Interview question 1:

Interview question 2:

Interview question 3:

5, Flexible array

1. Preface

2. Features

3. Advantages

I Memory partition

To understand dynamic memory management, you must first understand the main forms of memory division by C program:

Stack area

① When the function is executed, the storage units of local variables in the function can be created on the stack, and these storage units are automatically released at the end of function execution.

② Stack memory allocation is built into the instruction set of the processor, which is very efficient, but the allocated memory capacity is limited. (stack overflow problem)

③ The stack area mainly stores the local variables, function parameters, return data, return address, etc. allocated for running the function.

④ Downward growth means that the addresses applied from the stack are decreasing

Heap area

① Generally, it is released by the programmer. If the programmer does not release it, it may be recycled by the OS(operate system) at the end of the program. The allocation method is similar to a linked list.

② Dynamic memory is opened up on the heap.

③ Upward growth means that the addresses applied for in turn from the heap are increasing

Data segment (static area)

① Store global variables and static data.

② Released by the system at the end of the program.

Code snippet

① Store the binary code of the function body (class member function and global function).

② The data can only be read and cannot be modified

  (source: bit technology)

II Four functions

①malloc

Function: apply for a space of size_t size (unit byte) in heap area. Successfully return the address of dynamic opening space, and fail to return NULL pointer  . (for example, if the development space is too large, it will fail)

  Note: 1. The case where the size is 0 is undefined, and the result depends on the processing of the compiler

                2. Since the return value of malloc is of void * type, it is best to force type conversion when receiving with pointer

                3. Beware of memory development failure and return null pointer

#include
int main()
{
	int*p = (int *)malloc(40);
	return 0;
}

Look! We can easily and dynamically open up a space of a specified size. But I don't know if you have such a doubt: if we keep opening up space, will the memory of the computer become smaller and smaller? In fact, when our program ends, the dynamically opened space will be automatically recycled. Of course, we can also choose to take the initiative - use the free function.

②free

Function: release the dynamically opened space (memblock is the pointer to the dynamically opened space)

Note: 1.free(NULL), the function does not perform any operation

                2. You cannot use the free function to release non dynamic space

                3.free just frees up space and doesn't clear the pointer. That means you broke up with your girlfriend, but you're still in prison                     Remember the phone number (spatial address) of others. If you dereference the pointer at this time, you will commit an illegal memory access                   So we should assign the pointer to null in time and give up completely to others.

#include
#include
int main()
{
	int i = 0;
	int*p = (int *)malloc(10*sizeof(int));
    assert(p);//Monitor whether the development is successful
	for (i = 0; i < 10; i++)
	{
		p[i] = i;//Or * (p+i), but not p + +.
	}
	for (i = 0; i < 10; i++)
	{
		printf("%d ",p[i]);
	}
	free(p);
	p = NULL;//Preferably set to NULL
	return 0;
}

  The combination of malloc and free perfectly completes the process of dynamic memory development, but in fact, dynamic memory development is not only the privilege of malloc, but also of calloc, but their functions are different

③calloc

Function: apply for a piece of space from the heap to store num size elements. The pointer to the open space is returned successfully, and NULL is returned if it fails.

Difference: calloc initializes each element to 0 after opening up the space. Therefore, malloc is more efficient in opening up the space. Calloc will automatically assign a value of 0, and each has its own advantages.

  calloc - initialize to 0

  malloc - uninitialized

Knowing malloc, calloc and other functions for dynamic space development, you may think of what to do if the space is not enough. Don't worry, C language gives the realloc function to adjust the size of dynamic space development.  

④realloc

  Function: adjust the dynamic memory size to size (unit byte)

  Note that there are two cases of realloc resizing:

Case 1:

  If the original space is sufficient, the space will be added directly after the original memory, and the data of the original space will not change.

Case 2:  

When there is not enough space after the original space, the expansion method is to find another continuous space of appropriate size on the heap space, copy the original content and add it. In this way, the function returns a new memory address.

The space pointed to by p created by malloc

  Space pointed by p1 after realloc

Space pointed to by p after realloc

  Based on the above analysis, we can draw the following conclusions (when the space is insufficient):

1. Return a new address to the newly opened space

2. The contents of the original space will be copied to the newly opened space

3. The content of the original space is released

  When the incoming pointer is a null pointer, the function of realloc is equivalent to that of malloc.

III Error prone analysis

Try to analyze the defects or deficiencies in the following code

Question 1:

#include
#include
//Defective version
int main()
{
	int i = 0;
	int*p = (int *)malloc(5*sizeof(int));
	for (i = 0; i < 5; i++)
	{
		p[i] = i;
	}
	p = realloc(p, 1000000);//ok?
	free(p);
	p = NULL;
}



//Perfect version
int main()
{
	int i = 0;
	int*p = (int *)malloc(5*sizeof(int));
    assert(p);
	for (i = 0; i < 5; i++)
	{
		p[i] = i;
	}
	int *p1 = (int *)realloc(p, 1000);
    if(p1!=NULL)
    {
        p=p1;//For the sake of program coherence, it is best to assign values to the first p
    }
	free(p);
	p = NULL;
}

1. Both malloc and realloc should be careful when memory development fails and returns NULL, so verification is essential

2. If the writing of "p = realloc(p, 1000000);" fails, the original address can not be found. It can be said that you have lost your wife and lost your soldiers.

Question 2:  

void test()
{
	int i = 0;
	int *p = (int *)malloc(10 * sizeof(int));
	if (NULL == p)
	{
		exit(EXIT_FAILURE);
	}
	for (i = 0; i <= 10; i++)//ok?
	{
		*(p + i) = i;
	}
	free(p);
}

  1. Pay attention to the problem of cross-border access, "I < 10" will not crash the program

Question 3:

void test()
{
	int a = 10;
	int *p = &a;
	free(p);//ok?
}

  1. free cannot be used to release memory that is not dynamically developed

Question 4:

void test()
{
	int *p = (int *)malloc(100);
	p++;//ok?
	free(p);
}

  The address of 1.p cannot be changed, otherwise a free space error will occur during free

Question 5:  

void test()
{
	int *p = (int *)malloc(100);
	free(p);
	free(p);
}

1. It is obvious that the procedure made the mistake of repeated release

2. Problems can be easily found in this short code, but how can we avoid them in a long code?

    ① Do who opens up who releases space

    ② Form the habit of setting the pointer to NULL, so that even if it is free, it is free (NULL). As mentioned above, nothing happens

Question 6:

void test()
{
	int *p = (int *)malloc(100);
	if (NULL != p)
	{
		*p = 20;
	}
}
int main()
{
	test();
	while (1);
}

1. When executing the above program, you can open the task manager to find that the CPU occupied space has been increasing (the computer has a protection mechanism and stops growing to a certain extent), and the space has been opened up but not released, resulting in less and less free memory. This is also what we call memory leakage

2. Therefore, the dynamically opened memory must be released and released correctly.

4, Classic interview questions

interview Question 1:

void GetMemory(char *p)
{
	p = (char *)malloc(100);
}
int main()
{
	char *str = NULL;
	GetMemory(str);
	strcpy(str, "hello world");
	printf(str);
	return 0;
} 

  What is the result of the operation?               Program crash

  Analysis: it is easy for beginners to enter this misunderstanding and think that STR success points to the open space. But when you think about it, is STR really what we think? Here, we confuse address transfer operation and parameter transfer operation. The operation in the figure belongs to parameter transfer operation. p is just a temporary copy of str. changing p has no impact on str. Of course, the above code also has the error of not checking the return value and not actively releasing memory.

How can we correct it? Let's do a detailed answer in the next interview question.

Interview question 2:

char *GetMemory(void)
{
	char p[] = "hello world";
	return p;
}
int main()
{
	char *str = NULL;
	str = (char*)GetMemory();
	printf(str);
	return 0;
}

What is the result of the above procedure? Print random values.

Analysis: if you think the GetMemory function returns the address of P, yes, you're right. But at this time, the content pointed to by P is no longer "hello world". Why? P [] the array is created in the function. As mentioned at the beginning of the article, the local variables applied by the function are opened in the stack area, and the characteristic of the variables opened in the stack area is that when the function ends, the variables are destroyed immediately, so p just remembers the address at that time, and the content in it is no longer "hello world". If you dereference str at this time, an illegal memory access error will occur.

Interview question 3:

int main(void)
{
	char *str = (char *)malloc(100);
	strcpy(str, "hello");
	free(str);
	if (str != NULL)
	{
		strcpy(str, "world");
		printf(str);
	}
	return 0;
}

What is the result of the above procedure? Print "world"

Analysis: I don't know if any partners think there is no print result. If so, I guess they misunderstood the role of free. Free just frees the memory in the heap area and does not assign the address pointed to by str as a null pointer.

How to modify the interview procedure:

After analyzing the above three interview questions, I believe you have a deeper understanding of dynamic memory development. Let's return to the first question, how to correct it? In fact, the reason why the code in interview question 1 failed is that there is no practice between p and str. taking this as a breakthrough, we can have the following two ideas: 1. Address transmission operation   2. Return the address of p.

//Using address transfer operation
void GetMemory(char **p)
{
	*p = (char *)malloc(100);
}
int main()
{
	char *str = NULL;
	GetMemory(&str);
	if (str != NULL)
	{
		strcpy(str, "hello world");
		printf(str);
	}
	free(str);
	str = NULL;
	return 0;
}

//Returns the address of p
char* GetMemory(char *p)
{
	p = (char *)malloc(100);
	return p;
}
int main()
{
	char *str = NULL;
	str=(char *)GetMemory(str);
	if (str != NULL)
	{
		strcpy(str, "hello world");
		printf(str);
	}
	free(str);
	str = NULL;
	return 0;
}

V Flexible array

1. Preface

   We want to realize the dynamic change of array length. Can the following code?  

const int n = 0;
int main()
{
	int arr[n] = { 0 };
	return  0;
}

Obviously not. It must be a constant in []. Even if const is added, it is still a variable, but this variable cannot be modified. Can we use a pointer to the dynamically opened space? This method is indeed possible, but here we will introduce a better method - flexible array (new in C99). We will compare the two in the end.

2. Features

1. The flexible array member in the structure must be in the last.
2. The size of this structure returned by sizeof (struct s) does not include the size of the flexible array.
3. The structure containing flexible array members uses malloc() function to dynamically allocate memory, and the allocated memory should be larger than the size of the structure to adapt to the expected size of the flexible array.

Use demonstration:

struct s
{ 
	int n;
	int arr[0];
};
int main()
{
	int i = 0;
	struct s*p = (struct s*)malloc(sizeof(struct s)+40);//Note that the size of struct does not include dynamic arrays
	if (p != NULL)
	{
		for (i = 0; i < 10; i++)
		{
			p->arr[i] = i;
		}
	}
	free(p);
	p = NULL;
	return 0;
}

3. Advantages

Let's try to implement the above functions with pointers first

#include
struct s
{ 
	int n;
	int *arr;
};
int main()
{
	struct s*ps =(struct s*) malloc(sizeof(struct s));
    ps->n=10;
	if (ps != NULL)
	{
		ps->arr = (struct s*)malloc(ps->n*sizeof(int));//Receiving the address of opening up space with arr
		if (ps->arr != NULL)
		{
			int i = 0;
			for (i = 0; i < 10; i++)
			{
				ps->arr[i] = i;
			}
		}
	}
	free(ps->arr);
	ps->arr = NULL;
	free(ps);
	ps = NULL;

	return 0;
}

Compared with dynamically opening up array space with pointers, flexible arrays have the following three advantages:

1. It is more convenient to release space

If our code is in a function for others, you make a secondary memory allocation in it and return the whole structure to the user. The user can release the structure by calling free, but the user does not know that the members in the structure also need free, so you can't expect the user to find it. 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 free all the memory once.

2. Reduce memory fragmentation

n and arr created using flexible arrays are created together and are spatially continuous. The structure created with pointer and the pointer pointing space in the structure are discontinuous, and there are often wasted memory fragments

3. Improve access speed

CPU read speed in computer           Hard disk < memory < cache < register

When reading data, the computer follows the "locality principle", that is, 80% of the next accessed memory is near the current memory, so the register will read the surrounding data in advance. If the data is discontinuous, it means that the register hit fails, so it is necessary to search from the cache to the internal memory or even to the hard disk until it is found. The speed is naturally slow.

Tags: Linux Operation & Maintenance security

Posted on Wed, 17 Nov 2021 20:19:03 -0500 by wil831