Technology Sharing

C Language from Scratch 33 - Memory Management (I)

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

I. Basic Situation
The memory management of C language is divided into two parts: system management and manual management by programmers. The memory managed by the system mainly refers to the variables inside the function (local variables). These variables enter the memory when the function is running. This memory area is called the "stack area". After the function is finished running, the operating system automatically unloads it from the memory. The memory managed by the user manually mainly refers to the variables that exist throughout the entire process of the program running (global variables). The memory area where this part is located is called the "heap area". These variables need to be manually released from the memory by the user. If you forget to release it after use, it will continue to occupy the memory until the program exits and is handed over to the operating system for cleaning.
2. void pointer
The memory space in the computer is assigned a unique address code, and the specified memory area can be found through the memory pointer. Pointer variables need to have a type, such as: int* p=&x; declares an int pointer, which points to the address of the integer variable x. When the compiler gets the data from this address, it knows that the stored data is int type data. When you are not sure what type of data is stored and just want to find a memory address first, you use a void type pointer. A void type pointer is also called an indefinite type pointer, which can point to any type of data.
Pointers of void type can be converted to and from other types of pointers, and there is nothing special about their usage, except that the stored content cannot be parsed using the "*" method.
int x = 10; //declare integer variable x
void* p = &x; //Declare a void pointer and point it to the address of x, indicating that the int pointer (&x) is converted to a void pointer.
int* q = p; // void pointer is converted back to integer pointer
printf( "%in", *q ); // Run result: 10
Note the difference between void* and void: void is typeless, and when used to declare a function it indicates no return value; whereas void*, although typeless, has a pointer, and something is returned.
3. Functions related to memory management
1、malloc()
This function can be remembered separately. m is the abbreviation of memory, and alloc is the abbreviation of allocation. Together, it means allocating memory.
Function: apply for a continuous memory block from the heap area (reminder: the heap area memory needs to be managed by the programmer and must be destroyed after use, so always remember this); usage format: malloc (see 1);
Parameter: Parameter 1 is the size of the memory block (a non-negative integer of type size_t, the return value of sizeof is of this type)
Return value: void* pointer (we will use the same type of pointer to receive the requested memory)
Usage: Variables declared using the malloc function are in the heap area, so anonymous declaration is often used, that is, instead of addressing specific variables, malloc directly returns a pointer, as shown below:
int* ptr = (int*) malloc(sizeof(int)); //Declare an int pointer variable in the heap area
The forced conversion in brackets is not necessary. malloc returns void*, which can be received as int*. The forced conversion here makes the code easier to understand.
Precautions:
a. Use the malloc function to include<stdlib.h>Header files, such as those containing only<stdio.h> Compiles fine, but doesn't run properly, keep in mind.
b. Assign value to ptr: *ptr=1000;.
c. Use malloc and free (which we will encounter later) together. Don’t forget free (ptr);.
d. The malloc address allocation may not be successful. If it is unsuccessful, it returns NULL. For this reason, you can add an if statement to handle it.
e. malloc only allocates memory, not initializes it. If initialization is required, another function needs to be called.
f. The most common use of malloc is to allocate memory for arrays and custom data structures.
2、 memset()
Function: Initialize each byte in the memory block pointed to by a pointer
Parameters: Parameter 1 Pointer to target memory Parameter 2 Assignment content (int ASCII code value or char character) Parameter 3 Assignment number of bytes (size_t type)
Return value: Returns the pointer to the target memory. Receiving it is meaningless, the same as parameter 1.
Usage: memset(ptrDestMem,'A',sizeof(char)*1024) //Assign character A to each byte of a memory block with a length of 1024 bytes
Precautions:
a. The header file to which this function belongs is<string.h> Some compilers can work normally without including this header file. If a runtime error occurs, please include it.
b. Note that memset operates on bytes (not bits); parameter 3 refers to how many bytes need to be assigned, which can be less than or equal to the number of bytes in the target memory, but cannot exceed the boundary.
c. The maximum value of parameter 2 cannot exceed what can be accommodated in one byte (int type 255), and it cannot be written if it exceeds.
2、free()
Function: A function paired with malloc to release the memory allocated by the malloc function to prevent memory leaks
Parameter: pointer returned by malloc function
Usage: free(ptr);
Precautions:
a. The header files needed to use free are also<stdlib.h> , don’t forget.
b. The free function can only be executed once. The same memory address cannot be released repeatedly, and the released pointer cannot be used again.
c. It is best to write malloc and free in pairs first, and then insert other codes in between to prevent forgetting.
 3、realloc()
re means again, and alloc means allocation. Together, it means reallocating memory
Function: Modify the size of the allocated memory block (increase or decrease).
Parameters: Parameter 1: pointer to the original memory block Parameter 2: size of the memory block after increase or decrease (still size_t type)
Return value: void* pointer (can be received by the original pointer type variable)
Usage: void* newPtr=realloc(ptr,sizeof(int)*10);
Precautions:
a. Using the realloc function also requires including<stdlib.h> .
b. The memory block pointed to by the original pointer has been replaced by the new pointer, so the new pointer should be freed when the program ends, and the original pointer does not need to be freed, otherwise a runtime error will occur.
c. realloc may fail to allocate memory and return NULL if it fails, so it is better to add an if statement to handle it
4. Application Examples

  1. #include<stdio.h>
  2. #include<stdlib.h> //malloc、free、realloc
  3. #include<string.h> //memset
  4. int main(void)
  5. {
  6. //A void指针的使用
  7. float PI = 3.14159;
  8. void* pPI = &PI;
  9. float r = 2.5;
  10. printf("半径2.5圆的面积:%fn", r * r * *(float*)pPI);//*(float*)pPI—pPI强转为float指针后再解出指向的值
  11. //B malloc、memset、free函数使用
  12. int n = 8;
  13. void* pMyMem = malloc(sizeof(char)*n); //char占1字节,malloc分派n字节的内存,返给void指针
  14. if (pMyMem == NULL)return 0; //若分派内存失败,程序结束
  15. void* myMem2=memset(pMyMem,'B',sizeof(char)*8); //对malloc分派的内存区块个字节都写入字符'B'。
  16. //并用myMem2接收返回的指针
  17. if (pMyMem == myMem2)printf("memset写入区块指针与其返回的指针相同n");
  18. char* transCh = pMyMem; //将void指针转成char型指针
  19. printf("显示写入内容:");
  20. for (int i = 0; i < n; i++)
  21. {
  22. printf("%c ", *transCh); //显示写入的字符
  23. transCh++;
  24. }
  25. printf("n");//换行
  26. free(pMyMem); //释放malloc函数分派的内存
  27. //free(ch); //只有malloc分派的内存才需要释放,且只释放一次(这里会报错)
  28. //C realloc、free函数的使用
  29. int* pMem = malloc(sizeof(int)*5);
  30. if (pMem == NULL)return 0;
  31. for (int i = 0; i < 5; i++) pMem[i] = (i+1)*10;
  32. for (int i = 0; i < 5; i++) printf("%i ", pMem[i]);
  33. printf("n重新增加分派内存后n");
  34. int* newpMem = realloc(pMem, sizeof(int) * 10);
  35. if (newpMem == NULL)return 0;
  36. for (int i = 0; i < 10; i++) newpMem[i] = (i + 1) * 10;
  37. for (int i = 0; i < 10; i++)printf("%i ", newpMem[i]);
  38. free(newpMem); //释放realloc的内存
  39. //free(pMem);//pMem已被重置,不能再释放,此语句会造成运行失败
  40. getchar(); //暂停屏幕
  41. return 0;
  42. }