Hand torn C language - dynamic memory management

catalogue

Introduction to dynamic memory functions

malloc and free

calloc

realloc

Common dynamic memory errors

Several classic written test questions

Memory development of C/C + + programs

Flexible array

Features of flexible array:

Use of flexible arrays

Advantages of flexible arrays

Why is there dynamic memory allocation

The memory development methods we have mastered include:

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

However, the above-mentioned way of opening up space has two characteristics:

  1. The size of the space is fixed.
  2. When declaring an array, you must specify the length of the array, and the memory it needs will be allocated at compile time.
     

But the demand for space is not just the above situation. Sometimes the size of the space we need can only be known when the program is running, and the way of opening up space during array compilation can not be satisfied. At this time, you can only try dynamic storage development.
 

Introduction to dynamic memory functions

malloc and free

C language provides a function for dynamic memory development:

void* malloc (size_t size);

This function requests a continuously available space from memory and returns a pointer to this space.

  • If the development is successful, a pointer to the developed space is returned.
  • If the development fails, a NULL pointer is returned, so the return value of malloc must be checked.
  • The type of the return value is void *, so the malloc function does not know the type of open space, which is determined by the user when using it.
  • If the parameter size is 0, malloc's behavior is standard and undefined, depending on the compiler.

C language provides another function free, which is specially used for dynamic memory release and recovery. The function prototype is as follows:

void free (void* ptr);

The free function is used to free dynamic memory.

  • If the space pointed to by the parameter ptr is not dynamically opened up, the behavior of the free function is undefined.
  • If the parameter ptr is a NULL pointer, the function does nothing.

malloc and free are both declared in the stdlib.h header file. for instance:

#include <stdio.h>
int main()
{
    //Code 1
    int num = 0;
    scanf("%d", &num);
    int arr[num] = {0};
    //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?
    return 0;
}

calloc

C language also provides a function called calloc, which is also used for dynamic memory allocation. The prototype is as follows:

void* calloc (size_t num, size_t size);
  • The function is to open up a space for num size elements, and initialize each byte of the space to 0.
  • The only difference between calloc and malloc is that calloc initializes each byte of the requested space to all zeros before returning the address.

for instance:

#include <stdio.h>
#include <stdlib.h>
int main()
{
    int *p = calloc(10, sizeof(int));//Open up 10 spaces with the size of sizeof(int),
                                     //And initialize each byte in the space to 0
    if(NULL != p)
    {
        //Use space
    }
    free(p);
    p = NULL;
    return 0;
}

Therefore, how to initialize the contents of the applied memory space? It is convenient to use the calloc function to complete the task.

realloc

  • The emergence of realloc function makes dynamic memory management more flexible.
  • Sometimes we find that the space applied for in the past is too small, and sometimes we feel that the space applied for is too large. In order to make the memory reasonable, we will flexibly adjust the size of the memory. The realloc function can adjust the size of dynamic memory.

The function prototype is as follows:

void* realloc (void* ptr, size_t size);
  • ptr is the memory address to be adjusted
  • New size after resizing
  • The return value is the adjusted memory starting position.
  • This function will also move the data in the original memory to a new space on the basis of adjusting the size of the original memory space.
  • realloc adjusts memory space in two ways:
  1. Case 1: there is enough space behind the original space
  2. Case 2: there is not enough space after the original space

In case 1, if you want to expand the memory, you can directly add space after the original memory, and the data in the original space will not change.

In 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. In this way, the function returns a new memory address. Due to the above two cases, we should pay attention to the use of realloc function.

for instance:

#include <stdio.h>
int main()
{
    int *ptr = malloc(100);
    if(ptr != NULL)
    {
        //Business processing
    }
    else
    {
        exit(EXIT_FAILURE);
    }
    //Expansion capacity
    //Code 1
    ptr = realloc(ptr, 1000);//Is this ok? (what happens if the application fails?)
    //Code 2
    int*p = NULL;
    p = realloc(ptr, 1000);
    if(p != NULL)
    {
        ptr = p;
    }
    //Business processing
    free(ptr);
    return 0;
}

Common dynamic memory errors

  • Dereference operation on 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);
}
  • Cross border access to dynamic open space
void test()
{
    int i = 0;
    int *p = (int *)malloc(10*sizeof(int));
    if(NULL == p)
    {
        exit(EXIT_FAILURE);
    }
    for(i=0; i<=10; i++)
    {
        *(p+i) = i;//Cross border access when i is 10
    }
    free(p);
}
  • Use free release for non dynamic memory
void test()
{
    int a = 10;
    int *p = &a;
    free(p);//ok?
}
  • Use free to release a part of dynamic memory
void test()
{
    int *p = (int *)malloc(100);
    p++;
    free(p);//p no longer points to the beginning of dynamic memory
}
  • Multiple releases of the same dynamic memory
void test()
{
    int *p = (int *)malloc(100);
    free(p);
    free(p);//Repeated release
}
  • Dynamic memory forget release (memory leak)
void test()
{
    int *p = (int *)malloc(100);
    if(NULL != p)
    {
        *p = 20;
    }
}
int main()
{
    test();
    while(1);
}

Forgetting to free dynamic space that is no longer in use can cause memory leaks. Remember: the dynamic space must be released and released correctly.
 

Several classic written test questions

Topic 1: what will be the result of running the Test function?

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

Answer: the parameter of GetMemory function is the pointer variable str. malloc inside the function opens up a space of 100 bytes, and then assigns the address to p, trying to bring back the opened space with p. however, when passing the parameter, STR itself is passed instead of STR's address, so the value of STR will not be changed. Therefore, STR itself is still a null pointer, and a string copy of the null pointer is performed below, so an error will occur.

Solution: pass the str address when passing the parameter. The parameter is received as a secondary pointer. At the end of the Test function, you need to free the malloc space.

Note: the last line printf(str) is OK.
 

Topic 2: what will be the result of running the Test function?

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

Answer: This is a classic error: return the stack space address.

            The GetMemory function internally creates a p array and returns the address of P, which is received as a str pointer in the Test function. However, after the GetMemory function is completed, the P space is destroyed, and then it is used for it. There is a problem.
Solution: create p space with malloc in GetMemory function, but don't forget to release it in Test.

Topic 3: what will be the result of running the Test function?

void GetMemory(char **p, int num)
{
    *p = (char *)malloc(num);
}
void Test(void)
{
    char *str = NULL;
    GetMemory(&str, 100);
    strcpy(str, "hello");
    printf(str);
}

Answer: you can print "hello", but there is a memory leak. malloc opens up space, but the opened space is not released at the end of the Test function.

Solution: release str space at the end of the Test function and set str to NULL.

Topic 3: what will be the result of running the Test function?

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

Answer: question: use the released space. After releasing the space of str, str is not set to NULL, so an if statement will be entered later to copy the released space, and an error occurs.

Solution: after releasing str, set str to NULL.

Memory development of C/C + + programs

Several areas of C/C + + program memory allocation:
 

1. Stack: when the function is executed, the storage units of local variables in the function can be created on the stack. When the function is executed, these storage units can be created
The storage unit is automatically released. 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 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.
3. The data segment (static area) stores global variables and static data. Released by the system at the end of the program.
4. Code segment: the binary code that stores the function body (class member function and global function).

With this picture, we can better understand the example of static keyword modifying local variables in the introduction to C language.

In fact, ordinary local variables allocate space in the stack area. The characteristic of the stack area is that the variables created above are destroyed when they leave the scope. However, the variables modified by static are stored in the data segment (static area). The data segment is characterized by the amount created on it, which is not destroyed until the end of the program, so the life cycle becomes longer.

Flexible array
 

Perhaps you 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 a "flexible array" member.

For example:

typedef struct st_type
{
    int i;
    int a[0];//Flexible array member
}type_a;

Some compilers will report errors and cannot compile. You can change it to:

typedef struct st_type
{
    int i;
    int a[];//Flexible array member
}type_a;

Features of flexible array:

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

For example:

//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

Use of flexible arrays

//Code 1
int i = 0;
type_a *p = (type_a*)malloc(sizeof(type_a)+100*sizeof(int));
//Business processing
p->i = 100;
for(i=0; i<100; i++)
{
    p->a[i] = i;
}
free(p);

In this way, the flexible array member a is equivalent to obtaining a continuous space of 100 integer elements.
 

Advantages of flexible arrays

Type above_ A the structure can also be designed as:

//Code 2
typedef struct st_type
{
    int i;
    int *p_a;
}type_a;
type_a *p = 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 complete the same function, but the implementation of method 1 has two advantages: the first advantage is to facilitate 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.

The second advantage is that this is conducive to access speed.

Continuous memory is beneficial to improve access speed and reduce memory fragmentation. (in fact, I personally don't think it's much higher. Anyway, you can't run. You need to add the offset to address it.)

Tags: C

Posted on Wed, 29 Sep 2021 00:30:13 -0400 by silrayn