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