⭐ Celebrate the National Day - about advanced C language ⭐ Dynamic memory management + flexible array [recommended collection]

catalogue

preface

Why is there dynamic memory allocation

Introduction to dynamic memory functions

malloc and free functions

malloc function:

free function

calloc function

realloc function

Common dynamic memory errors

Several classic written test questions

Memory development of C/C + + programs

Flexible array

Characteristics of flexible array

Use and advantages of flexible arrays

preface

This article mainly explains:

  1. Considerations for the use of dynamic memory management
  2. There are explanations on dynamic memory management
  3. Use of flexible arrays

Why is there dynamic memory allocation

  • General ways of opening up space:
int val = 20;//Open up four bytes in stack space
char arr[10] = {0};//Open up 10 bytes of continuous space on the stack space
  • characteristic:
The size of the space is fixed( Array declaration must specify the length of the array, and the memory it needs is allocated at compile time)
But sometimes the size of the space we need can only be known when the program is running. The way to open up space during array compilation cannot be satisfied, so dynamic memory development comes

Introduction to dynamic memory functions

malloc and free functions

malloc function:

  • effect:
A function for dynamic memory development (the unit of development space is bytes)
  • definition:
void* malloc (size_t size);
  • be careful:
  1. This function requests a continuously available space from memory and returns a pointer to the space
  2. If the development is successful, a pointer to the developed space is returned, and the contents of the developed space are random values
  3. If the development fails, a NULL pointer is returned, so the return value of malloc must be checked
  4. The type of the return value is void *, so the malloc function does not know the type of space to be opened. The user will decide when using it (the mandatory type needs to be converted into the required pointer type to facilitate space access)
  5. If the parameter size is 0, malloc's behavior is standard and undefined, depending on the compiler

free function

  • effect:
It is specially used to release and recycle the dynamically developed memory
  • definition:
void free (void* ptr);
  • be careful:
  1. If the space pointed to by the parameter ptr is not dynamically opened up, the behavior of the free function is undefined
  2. If the parameter ptr is a NULL pointer, the function does nothing
Note: malloc and free All declared in stdlib.h In header file
  • Example:
#include <stdio.h>
int main()
{
 //Code 1
 int num = 0;
 scanf("%d", &num);
 int arr[num] = {0};//err
 //Code 2
 int* ptr = NULL;
 ptr = (int*)malloc(num*sizeof(int));
 if(NULL != ptr)//Determine whether the ptr pointer is null
 {
 int i = 0;
 for(i=0; i<num; i++)
 {
 *(ptr+i) = 0;
 }
 }
 free(ptr);//Freeing the dynamic memory pointed to by ptr
 ptr = NULL;//Is it necessary? be quite necessary
 return 0; }
  • Q: is it true that after free ptr, you still need to set ptr as a null pointer
  • For example:
  • Ask again: what happens if you forget the space address of free dynamic application

If it has not been used in the program, it will lead to the white occupation of memory (not a memory leak at this time)

If it is pointed to another address (or should be destroyed) without being free in the program, this address will never be found in the process (that is, memory leakage: the useless memory is getting larger and larger, and the operating system has to allocate memory to the process all the time)

Therefore, either take the initiative to free, or wait until the process ends (automatically free memory, not recommended)

calloc function

  • effect:

It is also a function used to open up dynamic memory

  • definition:
void* calloc (size_t num, size_t size);
  • be careful:
  1. The first parameter num is the number of open spaces, and the second parameter size is the size of each open space (in bytes)
  2. For the opened space, it will be initialized to 0 (and   The only difference between malloc is that each byte of the requested space is initialized to all zeros before returning the address
  • Example:
#include <stdio.h>
#include <stdlib.h>
int main()
{
     int *p = (int*)calloc(10, sizeof(int));
     if(NULL != p)
     {
         //Use space
     }
     free(p);
     p = NULL;
     return 0; 
}
  • effect:

realloc function

  • effect:

It is used to adjust the size of dynamic development space (large or small, which can be adjusted according to the needs)

  • definition:
void* realloc (void* ptr, size_t size);
  • be careful:
  1. Parameter ptr is the memory address to be adjusted (it must be the first address of the dynamic application space)
  2. New size after size adjustment (in bytes)
  3. The return value is the adjusted starting position of memory (forced type conversion is also required to access space)
  • There are two situations when realloc increases the memory space:

  • Case 1: there is enough space behind the original space
  1. To expand memory, add space directly after the original memory
  2. The original spatial data does not change
  3. realloc() will return the original pointer
  • Case 2: there is not enough space after the original space
  1. Find another space on the heap space (continuous space of appropriate size, enough space to be opened up) to open up
  2. At the same time, copy the memory stored in the original development space, and then return the original development space to the computer
  3. If the application is successful, the function returns a new memory address
  4. If the application fails, NULL will be returned. At this time, the original pointer is still valid
  • Example:
//Expansion capacity
#include <stdio.h>
int main()
{
 int *ptr = (int*)malloc(100);
 if(ptr != NULL)
 {
     //Business processing
 }
 else
 {
     exit(EXIT_FAILURE);    
 }
  • Wrong application:
 //Code 1
 ptr = (int*)realloc(ptr, 1000);//Is this ok? (what happens if the application fails?)

If the application fails and returns NULL, and then assigns ptr to NULL, the original dynamic development space address will be lost

  • Correct application:
 //Code 2
 int*p = NULL;
 p = realloc(ptr, 1000);
 if(p != NULL)
 {
 ptr = p;
 }
 //Business processing
 free(ptr);
 return 0; 
 }

Common dynamic memory errors

  • Example 1: dereference of NULL pointer
void test()
{
     int *p = (int *)malloc(INT_MAX/4);
     *p = 20;//If the value of p is NULL, there is a problem
     free(p);
}
  • Correct example:
void test()
{
    int *p = (int *)malloc(INT_MAX/4);
    if(p!=NULL)
    {
       *p = 20; 
    }
    free(p);
    p=NULL;
}

Note: for dynamic memory development, we must consider the failure of development

  • Example 2: cross border access to dynamic open space
void test()
{
 int i = 0;
 int *p = (int *)malloc(10*sizeof(int));
 if(NULL == p)
 {
//If the development fails, the process ends
 exit(EXIT_FAILURE);
 }
 for(i=0; i<=10; i++)
 {
 *(p+i) = i;//Cross border visit when i is 10 (illegal act)
 }
 free(p);
 p==NULL;
}

Note: cross border access is an illegal operation, so you must pay attention to whether the scope meets the requirements

  • Example 3: use free release for non dynamic memory
void test()
{
 int a = 10;
 int *p = &a;
 free(p);//ok?  
}

Note: free is illegal for non dynamic space

  • Example 4: use free to release a piece of dynamic memory
void test()
{
 int *p = (int *)malloc(100);
 p++;
 free(p);//p no longer points to the beginning of dynamic memory
}
  • be careful:
  1. free must be the first starting point for releasing memory
  2. ++/– in some cases, it has side effects. If it needs to be used in the above cases, it can be guaranteed to the original address for a backup
  • Example 5: multiple releases of the same dynamic memory
void test()
{
 int *p = (int *)malloc(100);
 free(p);
 free(p);//Repeated release
}
  • be careful:
  1. The released space must be emptied in time, and the same space cannot be released repeatedly. If it is emptied in time, free again is only a free empty address, and there will be no operation on the empty address free
  2. Even if there are two points to the same memory, only one can be free, because after the release, the original memory space has been released, and then release is repeated release
  • Example 6: dynamic memory forgetting to release (memory leak)
void test()
{
 int *p = (int *)malloc(100);
 if(NULL != p)
 {
 *p = 20;
 }
}
int main()
{
 test();
 while(1);
}

Note: as mentioned above, to form a good habit, you must be free and leave it blank

Several classic written test questions

  • Example 1:
void GetMemory(char *p) {
    p = (char *)malloc(100);
}
void Test(void) {
    char *str = NULL;
    GetMemory(str);
    strcpy(str, "hello world");
    printf(str);//Will you output hello world?
}
  • Explanation:
void GetMemory(char *p) {
    p = (char *)malloc(100);//The end p of the function is destroyed, and the dynamic open space address cannot be brought out
}
void Test(void) {
    char *str = NULL;
    GetMemory(str);//The value of str passed in is NULL, not the address of str passed in (only the address of str passed in can modify the object pointed to by str)
    strcpy(str, "hello world");//strcpy cannot copy null pointers, and an error will be reported
    printf(str);//A null pointer prints nothing
}
  • Example 2:
char *GetMemory(void) {
    char p[] = "hello world";
    return p; 
}
void Test(void) {
    char *str = NULL;
    str = GetMemory();
    printf(str);//Can you print?
}
  • Explanation:
char *GetMemory(void) {
//Character array on stack
    char p[] = "hello world";
//At the end of the function, the corresponding opened space will be returned to the computer. At this time, p is a dangling pointer (its operation is illegal)
    return p; 
}
void Test(void) {
    char *str = NULL;
    str = GetMemory();
    printf(str);//It is illegal to operate on a dangling pointer
}
  • Example 3:
void GetMemory(char **p, int num) {
    *p = (char *)malloc(num);
}
void Test(void) {
    char *str = NULL;
    GetMemory(&str, 100);
    strcpy(str, "hello");
    printf(str);//Can you print?
}
  • Explanation:
void GetMemory(char **p, int num) {
    *p = (char *)malloc(num);//Successfully modify the object pointed to by str and bring back the dynamically opened space address
}
void Test(void) {
    char *str = NULL;
    GetMemory(&str, 100);
    strcpy(str, "hello");
    printf(str);//hello successfully printed
}
  • Example 4:
void Test(void) {
    char *str = (char *) malloc(100);
    strcpy(str, "hello");
    free(str);
    if(str != NULL)
    {
        strcpy(str, "world");
        printf(str);
    }
}
//What will happen?
  • Explanation:
void Test(void) {
    char *str = (char *) malloc(100);
    strcpy(str, "hello");
    free(str);//Although free frees dynamic memory space, str content will not be modified, that is, str is a dangling pointer
//free dynamic open space, be sure to leave it empty (form a good habit)
    if(str != NULL)
    {//It is illegal to operate on a dangling pointer
        strcpy(str, "world");
        printf(str);//error
    }
}

Memory development of C/C + + programs

  • Example:
int globalVar = 1;
static int staticGlobalVar = 1;
int main()
{
	static int staticVar = 1;	
	int localVar = 1;
	int num1[10] = { 1,2,3,4 };
	char char2[] = "abcd";
	char* pChar3 = "abcd";
	int* ptr1 = (int*)malloc(4 * sizeof(int));
	int* ptr2 = (int*)calloc(4, sizeof(int));
	int* ptr3 = (int*)realloc(ptr2, 4 * sizeof(int));
	free(ptr1);
	free(ptr3);
	return 0;
}
  • Diagram:

  •   Several areas of C/C + + program memory allocation:
  1. 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
  2. heap: it is usually 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 linked list)
  3. The data segment (static area) stores global variables and static data (released by the system after the program is completed)
  4. Code snippet: the binary code that holds the function body (class member function and global function)

Flexible array

  • Concept:
C99 In, the last element in the structure is allowed to be an array of unknown size, which is called a "flexible array" member
  • Example:
//Example 1:
typedef struct st_type
{
 int i;
 int a[0];//Flexible array member
}type_a;
//Example 2:
typedef struct st_type
{
 int i;
 int a[];//Flexible array member
}type_a;

Characteristics of flexible array

  1. A flexible array member in a structure must be preceded by at least one other member
  2. The size of this structure returned by sizeof does not include the memory 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 and advantages of flexible arrays

  • Examples of flexible array usage:
//code1
typedef struct st_type
{
    int i;
    int a[0];//Flexible array member
}type_a;
    printf("%d\n", sizeof(type_a));//The output is 4
    int i = 0;
//Obtain a continuous space of 100 integer elements
    type_a *p = (type_a*)malloc(sizeof(type_a)+100*sizeof(int));
//Based on the size of the original structure + the size of the flexible array to be developed
//Business processing
    p->i = 100;
    for(i=0; i<100; i++) 
    {
        p->a[i] = i; 
    }
        free(p);
        p=NULL;
  • Examples of dynamic memory usage:
//Code 2
typedef struct st_type
{
    int i;
    int *p_a; 
}type_a;
    type_a *p = (type_a *)malloc(sizeof(type_a));
    p->i = 100; 
    p->p_a = (int *)malloc(p->i*sizeof(int));
    //Business processing
    for(i=0; i<100; i++) 
    {
         p->p_a[i] = i; 
    }
        //Free up space
        free(p->p_a);
        p->p_a = NULL;
        free(p);
        p = NULL;
  • The above code 1 and code 2 can perform the same function, but the implementation of method 1 has two advantages:
  • The first advantage is: 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
User call free The structure can be released, but the user does not know that the members in the structure also need to be released free , so you can't expect users 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 will make a free You can free up all the memory
  • The second advantage is that it is conducive to access speed
Continuous memory is beneficial to improve access speed and reduce memory fragmentation

Tags: C

Posted on Thu, 30 Sep 2021 21:50:48 -0400 by ctcp