Detailed explanation of C language - dynamic memory allocation (1)

Table of contents

Why does dynamic memory allocation exist?

Introduction to dynamic memory functions

malloc

free

calloc

realloc

Common dynamic memory errors

1. Dereference operation on NULL pointer

2. Out-of-bounds access to dynamically opened space

3. Use free release for non-dynamically allocated memory

4. Use free to release a part of dynamically allocated memory

5. Release the same dynamic memory multiple times

6. Dynamically allocated memory and forget to release it (memory leak)


Why does dynamic memory allocation exist?

Common memory allocation methods:
int val = 20;//Allocate four bytes on the stack space
char arr[10] = {0};//Open up 10 bytes of continuous space on the stack space

 The above method of opening up space has two characteristics:

  • Space opening size is fixed
  • When an array is declared, the length of the array must be specified, and the memory it requires is allocated at compile time.

This conventional method of creating space is not flexible enough and cannot create a space of a specified size or adjust the size of the space that has been created.

This is where dynamic memory allocation comes in handy.

Introduction to dynamic memory functions

  • malloc

The function malloc for dynamic memory opening : void* malloc (size_t size);  is declared in the stdlib.h header file.

This function applies for a continuous available space in memory and uses the `malloc` function to allocate the required memory space. `malloc` takes one argument, the number of bytes you want to allocate . It returns a pointer to the allocated memory block . Remember to convert the returned pointer to the data type you need .

int* p = (int*)malloc(40);
  • If the parameter size is 0 , the behavior of malloc is undefined by the standard and depends on the compiler. 
  • If the development fails, a NULL pointer will be returned, so the return value of malloc must be checked.

First, let’s introduce the memory partitions:

The memory is divided into stack area, heap area and static area

  • The stack area is used for local variables
  • The heap area is used for dynamic memory allocation and formal parameters
  • The static area is used for global variables and static variables

 Let’s look at this example:

int main()
{
	//int arr[10];
	int* p = (int*)malloc(40);

	if (p == NULL)
	{
		perror("malloc");
		return 1;
	}
	//开辟成功
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d\n", *(p + i));
	}

	free(p);
	p = NULL;

	return 0;
}

 Successfully opened up 40 bytes of space and accessed 10 bytes of space: 

  • This code uses the malloc function to open up a 40-byte space in the heap area of ​​​​the memory, and stores the first address of the opened space in p in the stack area. When p points to the first address of the opened space, you can access this space.

  • After malloc applies for the space, it directly returns the starting address of the space and does not initialize the contents of the space.

  • When using  dynamically allocated memory , if  mallocthe allocation fails (usually due to insufficient memory), malloc a null pointer (NULL) will be returned. If (p == NULL) determines whether it is null, and if it is null, use the perror function to print an error message.

  • If you still don’t know what the perror function is, you can read my article about the perror function .
  • The space requested by malloc is returned to the operating system when the program exits. When the program does not exit, the dynamically applied memory will not be actively released. At this time, you need to use the free function to release.
  • free

    The free function is used to release dynamically allocated memory. Declared in the stdlib.h header file.
  • If the space pointed to by parameter is not dynamically allocated, the behavior of the free function is undefined.
  • If parameter is a NULL pointer, the function does nothing.
  • After using free, the parameter p will become a wild pointer. We can turn it into a null pointer and assign it a value of NULL.
  • calloc

The calloc function is also used for dynamic memory allocation: void* calloc ( size_t num , size_t size );

  • calloc is declared in the stdlib.h header file.
  • The function of the function is to open up a space for num elements of size size , and initialize each byte of the space to 0 .
  • The only difference from the function malloc is that calloc will initialize each byte of the requested space to all 0s before returning the address.
int main()
{
	int* p = (int*)calloc(10, sizeof(int));
	if (p == NULL)
	{
		perror("calloc");
		return 1;
	}
	//打印数据
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", p[i]);
	}
	//释放
	free(p);
	p = NULL;

	return 0;
}

Output result: 

  • realloc

The emergence of the realloc function makes dynamic memory management more flexible.

Sometimes we find that the space we applied for in the past is too small, and sometimes we feel that the space we applied for is too large. In order to maintain a reasonable amount of memory, we will definitely make flexible adjustments to the memory size. The realloc function can adjust the dynamically allocated memory size.
void* realloc ( void* ptr , size_t size ), realloc is declared in the stdlib.h header file.
  • ptr is the memory address to be adjusted
  • New size after size adjustment
  • The return value is the memory starting position after adjustment.
  • This function not only adjusts the size of the original memory space, but also moves the data in the original memory to the new space.
int main()
{
    int* p=(int*)malloc(40);
    if(p == NULL)
    {
        perror("malloc");
        return 1;
    }

    for(int i=0;i<10;i++)
        p[i] = i + 1;

    p = realloc(p,80);
    return 0;
}

 

If there is enough space at the back, add space directly at the back and return the starting address of the old space p. The data in the original space will not change .

  • Open up new space to open up the required bytes
  • Data from the old space will be copied to the new space
  • Free up old space
  • Returns the starting address of the new space

Case 3: Failure to open and return null pointer

If realloc fails to open and returns a null pointer, p = realloc(p,80); such assignment will directly cause the original data of p to be lost. We can create another variable to receive the return value of realloc. At the same time, an if statement is added to determine whether the development fails and the return value is a null pointer.

int main()
{
	int* p = (int*)malloc(40);
	if (p == NULL)
	{
		perror("malloc");
		return 1;
	}
	//初始化为1~10
	int i = 0;
	for (i = 0; i < 10; i++) 
	{
		p[i] = i + 1;
	}
	//增加一些空间
	int* ptr = (int*)realloc(p, 8000);
	if (ptr != NULL)
	{
		p = ptr;
		ptr = NULL;
	}
	else
	{
		perror("realloc");
		return 1;
	}
	//打印数据
	for (i = 0; i < 20; i++)
	{
		printf("%d\n", p[i]);
	}
	//释放
	free(p);
	p = NULL;

	return 0;
}

Let's debug and take a look at the ptr and p addresses: When ptr receives the realloc space, the address at this time is as follows

 

When running to p = ptr, when the space of p is insufficient, realloc opens up space for p, and the address of p is the same as ptr.

Common dynamic memory errors

1.  Dereference operation of NULL pointer

void test()
{
    int *p = (int *)malloc(INT_MAX/4);
    *p = 20;
    free(p);
}

If the value of p is NULL, there will be a problem, so you must judge whether p is a null pointer.

if (p == NULL)
	{
		perror("malloc");
		return 1;
	}

2. Out-of-bounds access to dynamically opened space

void test()
{
    int i = 0;
    int *p = (int *)malloc(10*sizeof(int));
    if(NULL == p)
    {
        exit(EXIT_FAILURE);    
    }
    for(i=0; i<20; i++)
    {
        *(p+i) = i;//当i大于10的时候越界访问
    }
    free(p);
    p = NULL;
}

A total of 10 integer spaces are allocated, but trying to access 20 integer data will cause out-of-bounds access. 

3. Use free release for non-dynamically allocated memory

void test()
{
    int a = 10;    
    int *p = &a;
    free(p);//ok?
}

4. Use free to release a part of dynamically allocated memory

int main()
{
	int* p = (int*)malloc(40);
	if (p == NULL)
	{
		perror("malloc");
		return 1;
	}
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		*p = i;
		p++;
	}
	//释放
	free(p);
	p = NULL;

	return 0;
}

The pointer does not point to the starting position. 

5. Release the same dynamic memory multiple times

int main()
{
	int* p = (int*)malloc(40);
	if (p == NULL)
	{
		return 1;
	}
	//使用

	free(p);
	free(p);
	return 0;
}

  p = NULL; Develop a good habit after releasing the memory and assign p to a null pointer 

6. Dynamically allocated memory and forget to release it (memory leak)

void test()
{
	int* p = (int*)malloc(100);
	if (NULL != p)
	{
		*p = 20;
	}
}

int main()
{
	test();
	while (1);

	return 0;
}

p is a local variable of the function and is destroyed when the function exits. However, the space requested by malloc is still there and cannot be released.

The dynamically applied memory space will not be automatically destroyed ( returned to the operating system) because it goes out of scope.

There are only two ways to destroy:

  1. freerelease
  2. Program ends (exit)

summary:

 The road to learning is long and difficult. I hope you will keep reviewing and coding. You will definitely receive your favorite offers in the future! ! !

 Let’s move on to the next part of study: Detailed explanation of C language—dynamic memory allocation (2)

Guess you like

Origin blog.csdn.net/m0_73800602/article/details/133156874