Dynamic memory management

Catalogue

Introduction to dynamic memory function

Common mistakes

Flexible array

Introduction to dynamic memory function

malloc: apply for a continuous space like memory

void* malloc(size_t size);

If the development is successful, return a pointer to the developed space.

If the development fails, a NULL is returned

The return type is void * and needs to be defined by the user

When the size is 0, the standard is not defined

Free: used to free dynamic memory

void* free(void *ptr);

If the space pointed to by ptr is not dynamically opened up, the behavior of the free function is undefined.

If ptr is NULL, the function does nothing.

calloc: used for dynamic memory allocation and initialization.

void* calloc(size_t num, size_t size);

Open up a space for num elements with size, and initialize each byte of the space to 0.

realloc: adjust the size of dynamic development memory.

void* realloc(void* ptr, size_t size);

ptr: memory address to be adjusted.

Size: the adjusted size.

The return value is the starting position of the memory after adjustment.

On the basis of adjusting the size of memory space, move the original data to a new space.

When there is enough space behind the original space: the memory to be expanded will be added directly after the original memory, and the original data will not change.

When there is not enough space after the original space: find another continuous space of appropriate size on the heap space to use, so that the function returns a new memory address. And copy the original data.

malloc:  

#include <stdio.h>
#include <string.h>
#include <errno.h>

int main()
{
	int* p = (int*)malloc(sizeof(int) * 1000000000000000);
	if (p == NULL) {
		printf("%s\n", strerror(errno));
		return 0;
	}
	int i;
	for (i = 0; i < 10; i++) {
		*(p + i) = i;
	}
	for (i = 0; i < 10; i++) {
		printf("%d ", p[i]);
	}
	free(p);
	p = NULL;
	return 0;
}

#include <stdio.h>
#include <string.h>
#include <errno.h>

int main()
{
	int* p = (int*)malloc(sizeof(int) * 10);
	if (p == NULL) {
		printf("%s\n", strerror(errno));
		return 0;
	}
	int i;
	for (i = 0; i < 10; i++) {
		*(p + i) = i;
	}
	for (i = 0; i < 10; i++) {
		printf("%d ", p[i]);
	}
	free(p);
	p = NULL;
	return 0;
}

 

 calloc:

#include <stdio.h>
#include <string.h>
#include <errno.h>

int main()
{
	int* p = (int*)calloc(10000000000, sizeof(int));
	if (p == NULL) {
		printf("%s\n", strerror(errno));
		return 0;
	}
	int i;
	for (i = 0; i < 10; i++) {
		printf("%d ", p[i]);
	}
	free(p);
	p = NULL;
	return 0;
}

#include <stdio.h>
#include <string.h>
#include <errno.h>

int main()
{
	int* p = (int*)calloc(10, sizeof(int));
	if (p == NULL) {
		printf("%s\n", strerror(errno));
		return 0;
	}
	int i;
	for (i = 0; i < 10; i++) {
		printf("%d ", p[i]);
	}
	free(p);
	p = NULL;
	return 0;
}

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
int main()
{
	int* p = (int*)calloc(10, sizeof(int));
	if (p == NULL) {
		printf("%s\n", strerror(errno));
		return 0;
	}
	int i;
	for (i = 0; i < 10; i++) {
		p[i] = i;
	}
	for (i = 0; i < 10; i++) {
		printf("%d ", p[i]);
	}
	free(p);
	p = NULL;
	return 0;
}

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>

int main()
{
	int* p = (int*)realloc(NULL, sizeof(int) * 10);
	if (p == NULL) {
		printf("%s\n", strerror(errno));
		return 0;
	}
	int i;
	for (i = 0; i < 10; i++) {
		p[i] = i;
	}
	for (i = 0; i < 10; i++) {
		printf("%d ", p[i]);
	}
	free(p);
	p = NULL;
	return 0;
}

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>

int main()
{
	int* p = (int*)calloc(10, sizeof(int));
	if (p == NULL) {
		printf("%s\n", strerror(errno));
		return 0;
	}
	for (int i = 0; i < 10; i++) {
		p[i] = 10 - i;
	}
	int* ptr = (int*)realloc(p, sizeof(int) * 20);
	if (ptr == NULL) {
		printf("%s\n", strerror(errno));
		return 0;
	}
	else {
		p = ptr;
		ptr = NULL;
	}
	for (int i = 0; i < 10; i++) {
		p[i + 10] = i;
	}
	for (int i = 0; i < 20; i++) {
		printf("%d ", p[i]);
	}
	free(p);
	p = NULL;
	return 0;
}

Common mistakes

Dereference operation on NULL pointer

Cross border access to dynamic open space

Use free release for non dynamic memory

Use free to release a part of dynamic memory

Release the same block of memory multiple times

Dynamic memory forgetting to release (memory leak)

Dereference to NULL pointer:

#include <stdio.h>
#include <stdlib.h>
#include <limits.h>

int main()
{
	int* p = (int*)malloc(sizeof(int) * INT_MAX);
	for (int i = 0; i < 10; i++) {
		*(p + i) = i;
	}
	for (int i = 0; i < 10; i++) {
		printf("%d ", p[i]);
	}
	return 0;
}

Cross border access to dynamic open space:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

int main()
{
	char* p = (char*)malloc(sizeof(char) * 10);
	if (p == NULL) {
		printf("%s\n", strerror(errno));
		return 0;
	}
	for (int i = 0; i <= 10; i++) {
		*(p + i) = 'a' + i;
	}
	for (int i = 0; i <= 10; i++) {
		printf("%c ", p[i]);
	}
	free(p);
	p = NULL;
	return 0;
}

Use free release for non dynamic memory:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

int main()
{
	int a = 10;
	int* p = &a;
	free(p);
	p = NULL;
	return 0;
}

Use free to release a part of dynamic memory:

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>

int main()
{
	int* p = (int*)malloc(sizeof(int) * 10);
	if (p == NULL) {
		printf("%s\n", strerror(errno));
		return 0;
	}
	for (int i = 0; i < 10; i++) {
		*p = i + 1;
		p++;
	}
	free(p);
	p = NULL;
	return 0;
}

Release the same block of memory multiple times:

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>

int main()
{
	int* p = (int*)malloc(sizeof(int) * 10);
	if (p == NULL) {
		printf("%s\n", strerror(errno));
		return 0;
	}
	for (int i = 0; i < 10; i++) {
		p[i] = i + 1;
	}
	free(p);
	free(p);
	return 0;
}

Dynamic memory forgetting to release:

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>

int* test()
{
	int* p = (int*)malloc(sizeof(int) * 1000000000);
	if (p == NULL) {
		return 0;
	}

	return p;
}
int main()
{	
	int* ptr = test();
	return 0;
}

Flexible array

Flexible array: the last element in the structure is allowed to be an array of unknown size, which is called a member of the flexible array.

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

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

Features of flexible array:

At least one other member in front of the flexible array member in the structure.

sizeof returns the size of this structure, excluding 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 structure size to adapt to the expected size of flexible array.

#include <stdio.h>

typedef struct S1
{
	int n;
	int arr[0];
}S1;

typedef struct S2
{
	int n;
	int arr[];
}S2;
int main()
{
	printf("%d\n", sizeof(S1));
	printf("%d\n", sizeof(S2));

	return 0;
}

Operation results:

Use of flexible arrays:

#include <stdio.h>
#include <stdlib.h>

typedef struct st_type
{
	int i;
	int a[];
}type_a;

int main()
{
	int i = 0;
	type_a* p = (type_a*)malloc(sizeof(type_a) + 100 * sizeof(int));
	p->i = 100;
	for (i = 0; i < 100; i++) {
		p->a[i] = i;
	}
	free(p);
	p = NULL;
	return 0;
}

Non flexible array:

#include <stdio.h>
#include <stdlib.h>

typedef struct st_type
{
	int i;
	int* pa;
}type_a;

int main()
{
	type_a* p = (type_a*)malloc(sizeof(type_a));
	p->i = 100;
	p->pa = (int*)malloc(sizeof(int) * 100);
	for (int i = 0; i < 100; i++) {
		p->pa[i] = i;
	}
	return 0;
}

Advantages of flexible arrays:

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 doesn't know that the members of the structure also need free, so you can't expect the user to find this thing. Therefore, we allocate the memory of the structure and the memory of its members at one time, and return a structure pointer to the user. The user can free all the memory once.

Facilitate access to memory:

Continuous memory is beneficial to improve access speed and reduce memory fragmentation.

Title:

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);
}

The above function should be set to the return type and accepted. FALSE.

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;
	GetMemory();
	printf(str);
}

str is still a NULL pointer. str should accept the return value of GetMemory function. FALSE.

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);
}

str is a NULL pointer, which transfers the address of str into the function, dynamically applies for a certain space for str, and prints it. FALSE. The dynamically opened memory should be released and set to NULL to prevent memory leakage.

4. 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);
	}
}

It can print out world and release the dynamically opened memory without setting it to NULL. str is a random value. FALSE.

Keywords: C Back-end

Added by Goose87 on Sat, 26 Feb 2022 21:51:09 +0200