Dynamic memory management: malloc, calloc, realloc, free

Posted by irn3rd on Sat, 15 Jan 2022 17:28:47 +0100

1, Why is there dynamic memory allocation

πŸŽ— Before, we opened up space like this:

int i = 20; // Open up 4 bytes in stack space
char arr[10] = { 0 }; // Open up 10 bytes of continuous space in the stack space

characteristic

one ️⃣ The size of the open space is fixed

two ️⃣ When an array is declared, it must contain a constant value (specify the length of the array)

Summary
In the past, the way of opening up space is not flexible enough and has great limitations (sometimes the size of space we need can only be known when the program is running)
Therefore, this article mainly understands the functions used to open up space on the memory heap

2, Introduction to dynamic memory functions

πŸ’¦ malloc

β­• Function information




#include<stdio.h>
#include<stdlib.h>
int main()
{
	//Suppose you open up 10 integer spaces:
	
	int arr[10];//1. Stack area development
	
	int* p = (int*)void* p = malloc(10 * sizeof(int));//2. Development of stacking area
	
	/*-----------------Split line-----------------*/
	
	//use
	//1. Development failure
	if(p == NULL)
	{
		perror("main");
		return 0;
	}
	//2. Successful development
	int i = 0;
	for(i = 0; i < 10; i++)
	{
		*(p + i) = i;
	}
	//Print
	for(i = 0; i < 10; i++)
	{
		printf("%d ", p[i]);
	}
	//Recycle space
	free(p);
	p = NULL;
	return 0;
}

Summary

one ️⃣ malloc applies for a continuously available space from the memory, and returns the address pointing to that space when the development is successful; Failed to open. NULL returned

two ️⃣ Because the return value of malloc function is of void * type, when receiving with a pointer variable of a certain type, it is also necessary to force the type to be converted to the corresponding type

three ️⃣ If malloc development fails, illegal dereference may be performed on the null pointer, so the space opened by malloc must be checked

four ️⃣ After using the space opened by malloc, take the initiative to recycle the space. After reclaiming the space, the right to use the space is beyond your control, and you can find the space through the pointer. Therefore, in order to avoid illegal access, you usually take the initiative to set the pointer to the space to NULL

πŸ’¦ free

β­• Function information




#include<stdio.h>
int main()
{
	int a = 10;
	int* p = &a;
	free(p);//1.err, reclaim stack space
	p = NULL;
	
	free(NULL)//2. Equivalent to - > / / free (null)
	return 0;
}

Summary

one ️⃣ If the space pointed to by the parameter ptr is not dynamically opened up, the behavior of the free function is not defined by the standard

two ️⃣ If the parameter ptr is a NULL pointer, it is considered invalid code

three ️⃣ There are two ways to reclaim space: first, after the main function is completed, the opened space will be passively returned to the OS, but for a program that runs 24 hours a day, if it does not actively reclaim the unused space, the remaining space will be less and less. The second is to take the initiative to recycle the unused space

πŸ’¦ calloc

β­• Function information





#include<stdio.h>
#include<stdlib.h>
int main()
{
	//malloc and calloc are not actively initialized
	//1.malloc
	int* p = (int*)malloc(40);
	if(p == NULL)
		return 1;
	int i = 0;
	for(i = 0; i < 10; i++)
	{
		printf("%d\n", *(p + i));
	}
	free(p);
	p = NULL;
	
	//2.calloc
	int* q = (int*)calloc(10, sizeof(int));
	if(q == NULL)
		return 1;
	for(i = 0; i < 10; i++)
	{
		printf("%d\n", *(q + i));
	}
	free(q);
	q = NULL;
	return 0;
}

πŸ’¨ result:

Summary

one ️⃣ Compared with malloc, calloc will actively initialize the opened memory space

πŸ’¦ realloc

πŸŽ— The emergence of realloc makes dynamic memory management more flexible

When applying for space, we sometimes find that it is too large or too small and needs flexible adjustment: the function that can realize flexible adjustment is realloc, so realloc in malloc, calloc and realloc is the undisputed leader

β­• Function information




#include<stdio.h>
#include<stdlib.h>
int main()
{
	//1. Use calloc to open up 10 shaping sizes
	int* p = (int*)calloc(10, sizeof(int));
	if(p == NULL)
	{
		perror("main");
		return 0;
	}
	//2. Use open space
	int i = 0;
	for(i = 0; i < 10; i++)
	{
		*(p + i) = 5;
	}

	//3. Here, 10 integer spaces are needed, and the space pointed to by p has been used up, so use realloc to adjust the space	
	//Why is it designed like this? Please see the front for details:
	int* pnew = (int*)realloc(p, 20 * sizeof(int));
	if(pnew != NULL)
	{
		p = pnew;	
	}
	//2. Reclaim space
	free(p);
	p = NULL;
	
	return 0;
}

πŸ“ Detailed explanation:

πŸŽ— realloc development principle

❓❔ Thinking: how to properly receive the address of realloc

βœ–γ€€int* p = (int*)realloc(p, 20 * sizeof(int));
If you use the old address to receive: realloc may not find the appropriate space to adjust the size, and NULL is returned. At this time, if you give it to p, not only the space is not opened up well, but also the content of the old space can not be found
——Stealing chicken can't eat rice
 
βœ”γ€€int* pnew = (int*)realloc(p, 20 * sizeof(int));
Receive with the new address first. If the development is successful, assign it to the old space. This not only avoids the loss of the old space, but also applies to scenario 1 and scenario 2

πŸŽ— realloc can achieve malloc effect when used alone (without initialization)

#include<stdio.h>
#include<stdlib.h>
int main()
{
	int* p = (int*)realloc(NULL, 40);//Same as int* p = (int*)malloc(40);
	if (p == NULL)
		return 1;
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d\n", *(p + i));
	}
	free(p);
	p = NULL;
	return 0;
}

πŸ’¨ result:

3, Common dynamic memory errors

πŸ’¦ Dereference operation on NULL pointer

#include<stdio.h>
#include<stdlib.h>

int main01()
{
	int* p = (int*)malloc(10000000000);
	int i = 0;
	for(i = 0; i < 10; i++)
	{
		*(p + i) = i;//int* p = NULL;  If the development fails, it will illegally access memory
	}
	return 0;
}
/*--------------------Correct--------------------*/
int main()
{
	int* p = (int*)malloc(10000000000);
	if(p == NULL)
	{
		perror("main");
		return 0;	
	}
	int i = 0;
	for(i = 0; i < 10; i++)
	{
		*(p + i) = i;//int* p = NULL;  If the development fails, it will illegally access memory
	}
	return 0;
}

Summary

one ️⃣ The return values of malloc, calloc and realloc should be judged empty

πŸ’¦ Cross border access to dynamic open space

#include<stdio.h>
#include<stdlib.h>
int main()
{
	int* p = (int*)malloc(10 * sizeof(int));
	if(p == NULL)
	{
		return 0;	
	}
	int i = 0;
	for(i = 0; i < 40; i++)
	{
		*(p + i) = i;//Cross border visit
	}
	free(p);
	p = NULL;
	return 0;
}

πŸ’¦ Use free to free up non dynamic space

#include<stdio.h>
#include<stdlib.h>
int main()
{
	int arr[10] = { 0 };
	int* p = arr;
	int i = 0;
	for(i = 0; i < 10; i++)
	{
		*(p + i) = i;
	}
	free(p);//This is err's
	p = NULL;
	return 0;
}

πŸ’¦ Use free to release a part of dynamic memory

#include<stdio.h>
#include<stdlib.h>
int main01()
{
	int* p = (int*)malloc(10 * sizeof(int));
	if(p == NULL)
	{
		return 0;	
	}
	int i = 0;
	for(i = 0; i < 5; i++)
	{
		*p++ = i;
	}
	free(p);//Not fully recycled
	p = NULL;
	return 0;
}
/*--------------------Correct--------------------*/
int main()
{
	int* p = (int*)malloc(10 * sizeof(int));
	if(p == NULL)
	{
		return 0;	
	}
	//Just want to initialize the first five elements of the array to 0 1 2 3 4, as long as you don't let p really go back
	int i = 0;
	for(i = 0; i < 5; i++)
	{
		//1.
		p[i] = i;
		//2.
		//*(p + i) = i;
	}
	free(p);
	p = NULL;
	return 0;
}

Summary

one ️⃣ It is err to not fully release the dynamically opened space

two ️⃣ It may cause a memory leak because no one can remember the starting space

πŸ’¦ Multiple releases of the same dynamic memory

#include<stdio.h>
#include<stdlib.h>
int main01()
{
	int* p = (int*)malloc(100);
	//use
	//...
	//release
	free(p);
	//...
	//...
	free(p);//err
	return 0;
}
/*--------------------Correct--------------------*/
#include<stdio.h>
#include<stdlib.h>
int main()
{
	int* p = (int*)malloc(100);
	//use
	//...
	//release
	free(p);
	p = NULL;
	//...
	//...
	free(p);//meaningless
	return 0;
}

Summary

one ️⃣ After free completes malloc, calloc and realloc to open up space, it should be set to NULL in time

πŸ’¦ Dynamic memory forget release (memory leak)

#include<stdio.h>
#include<stdlib.h>
void test()
{
	int* p = (int*)malloc(100);//p is a local variable
	if(p == NULL)
		return;
	//use
	//...
}
int main01()
{
	test();
	//...	
	//Here is the memory leak:
	//Dynamically opened up space in test and forgot to release it. And the local variable p does not leave any last words, so it cannot be released outside the function. As long as the program is not dead, no one can find this space	
	return 0;
}

πŸ’¦ Schematic diagram of program memory area division in C/C + +

one ️⃣ Stack: when executing a function, the storage units of local variables in the function can be created on the stack. 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. The stack area mainly stores the local variables, function parameters, return data, return address, etc. allocated for running the function.
 
two ️⃣ heap: it is generally allocated and released by the programmer. If the programmer does not release it, it may be recycled by the OS at the end of the program. The allocation method is similar to a linked list.
 
three ️⃣ The data segment (static area) stores global variables and static data. Released by the system at the end of the program.
 
four ️⃣ Code snippet: the binary code that holds the function body (class member function and global function).

4, Several classic written test questions

πŸ’¦ 1.

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
void GetMemory(char* p)//p is a temporary copy of str
{
	p = (char*)malloc(100);//2. No release after dynamic space development; p is a local variable. When it is out of range, the open space cannot be found, so there is a memory leak 	 	
}
void Test(void)
{
	char* str = NULL;
	GetMemory(str);//pass by value
	strcpy(str, "hello world");//1. Same as strcpy(NULL, "hello world")
	printf(str);
}
int main01()
{
	Test();
	return 0;
}
/*--------------------Correction 1--------------------*/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
char* GetMemory(char* p)
{
	p = (char*)malloc(100); 	
	return p;//Before the local variable p is destroyed, the address pointing to the dynamically opened space is returned
}
void Test(void)
{
	char* str = NULL;
	str = GetMemory(str);
	strcpy(str, "hello world");
	printf(str);
	free(str);
	str = NULL;
}
int main02()
{
	Test();
	return 0;
}
/*--------------------Correction 2--------------------*/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
void GetMemory(char** p)
{
	*p = (char*)malloc(100); //*p found str	
}
void Test(void)
{
	char* str = NULL;
	GetMemory(&str);//Byref 
	strcpy(str, "hello world");
	printf(str);
	free(str);
	str = NULL;
}
int main()
{
	Test();
	return 0;
}

πŸ’¦ 2.

#include<stdio.h>
char* GetMemory(void)
{
	char p[] = "hello world";//p is a local variable, but the received content is created in the stack area
	return p;//Although the address of p is returned here, the content of this space has been destroyed
}
void Test(void)
{
	char* str = NULL;
	str = GetMemory(); 
	printf(str);//Illegal access to memory: now str's space is not its own, so it will be hot when printing
}
int main()
{
	Test();
	return 0;
}

πŸ’¦ 3.

#include<stdio.h>
int* f2(void)
{
	int* ptr;
	*ptr = 10;//Wild pointer problem: ptr is not initialized. At this time, there is a problem in dereferencing
	return ptr;
}
int main()
{
	f2();
	return 0;
}

πŸ’¦ 5.

#include<stdio.h>
#include<string.h>
void GetMemory(char** p, int num)
{
	*p = (char*)malloc(num);
}
void* Test(void)
{
	char* str = NULL;
	GetMemory(&str, 100)
	strcpy(str, "hello");
	printf(str);
}
int main01()
{
	Test();//No free
	return 0;
}
/*--------------------Correct--------------------*/
#include<stdio.h>
#include<string.h>
void GetMemory(char** p, int num)
{
	*p = (char*)malloc(num);
}
void* Test(void)
{
	char* str = NULL;
	GetMemory(&str, 100)
	strcpy(str, "hello");
	printf(str);
	free(str);//Correction
	str = NULL;
}
int main()
{
	Test();
	return 0;
}

πŸ’¦ 6.

#include<stdio.h>
#include<stdlib.h>
void Test(void)
{
	char* str = (char*)malloc(100);
	strcpy(str, "hello")
	free(str);
	if(str != NULL)//free is not automatically set to NULL
	{
		strcpy(str, "world");//str has been released. Illegal access to memory
		printf(str);
	}
}
int main01()
{	
 	Test();
	return 0;
}
/*--------------------Correct--------------------*/
#include<stdio.h>
#include<stdlib.h>
void Test(void)
{
	char* str = (char*)malloc(100);
	strcpy(str, "hello")
	free(str);
	str = NULL;//Active NULL
	if(str != NULL)
	{
		strcpy(str, "world");
		printf(str);
	}
}
int main()
{	
 	Test();
	return 0;
}

5, Flexible array

πŸ’¦ What is a flexible array

πŸŽ— Presumably, many people have never heard of the concept of flexible array, but it does exist. In C99, the last element in the structure is allowed to be an array of unknown size, which is called "flexible array" member. Note: this does not mean that all compilers can support it

struct S1
{
	int n;
	int arr[];//This is called a "flexible array" member
};
struct S2
{
	int n;
	int arr[0];//It can also be written like this
};

πŸ’¦ Characteristics of flexible array

one ️⃣ A flexible array member in a structure must be preceded by at least one member

two ️⃣ The size of this structure returned by sizeof does not include the memory of the flexible array

three ️⃣ 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

four ️⃣ The members of the flexible array are all on the heap

#include<stdio.h>
#include<stdlib.h>
struct S
{
	int n;
	int arr[];//1. There must be more than one member before
};
int main()
{
	struct S s = { 0 };
	printf("%d\n", sizeof(s));//2.4Byte

	//3. The expected size of arr is 10 integers
	struct S* ps = (struct S*)malloc(sizeof(struct S) + 10 * sizeof(int));
	if(ps == NULL)
	{
		return 0;	
	}
	//assignment
	ps->n = 10;
	int i = 0;
	for(i = 0; i < 10; i++)
	{
		 ps->arr[i] = 0;
	}
	//adjustment
	struct S* ptr = (struct S*)realloc(ps, sizeof(struct S) + 20 * sizeof(int));
	if(ptr != NULL)
	{
		ps = ptr;	
	}
	//release
	free(ps);
	ps = NULL;
	return 0;
}

πŸŽ— Seeing this, do you think that flexible arrays do not need to exist, as long as the pointer points to the dynamically opened space (simulate flexible arrays)

#include<stdio.h>
#inlude<stdlib.h>
struct S
{
	int n;
	int* arr;
};
int main()
{
	struct S* ps = (struct S*)malloc(sizeof(struct S));
	if(ps == NULL)
		return 0;
	ps-> n = 10;
	ps->arr = (int*)malloc(10 * sizeof(int))
	if(ps->arr == NULL)
		return 0;
	//assignment
	int i = 0;
	for(i = 0; i < 10; i++)
	{
		ps->arr[i] = i;
	}
	//increase
	int* ptr = (int*)realloc(ps->arr, 20 * sizeof(int));
	if(ptr != NULL)
	{
		ps->arr = ptr;
	}
	//release
	//Two spaces need to be recycled here, and the recycling must have priority
	free(ps->arr);
	ps->arr = NULL;
	free(ps);
	ps = NULL;
	return 0;
}

Comparison: flexible arrays ➰ Pointer analog flexible array

one ️⃣ Pointer simulation requires two malloc and two free, which means that it is easy to make mistakes; The flexible array only needs one malloc and one free

two ️⃣ Secondly, more malloc, more memory fragments, and lower memory utilization

πŸ’¨ Summarize the benefits of flexible arrays:

one ️⃣ Easy memory release

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.

two ️⃣ Conducive to access speed

Continuous memory is beneficial to improve access speed and reduce memory fragmentation. (in fact, it's not too high. Anyway, you can't run. You need to add the offset to address it.)

 

Recommend an article on flexible arrays
Member array and pointer in C language structure

Topics: C