Dynamic memory management

0. Preface

Why is there dynamic memory allocation?

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
 Such static variables are hard to tune,Drought death,Waterlogging death
  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.

The space requested for dynamic memory allocation is on the heap

1. Dynamic memory function

malloc

void *malloc( size_t size );

malloc returns a void pointer to the allocated space, or NULL if there is insufficient memory available. 
To return a pointer to a type other than void, use a type cast on the return value. The storage space pointed to by the return value is guaranteed to be suitably aligned for storage of any type of object. If size is 0, malloc allocates a zero-length item in the heap and returns a valid pointer to that item. Always check the return from malloc, even if the amount of memory requested is small.
malloc Returns a pointer to the allocated space void Pointer, if the available memory is insufficient, return NULL. To return a non void Type pointer, please type convert the return value. The storage space pointed to by the return value ensures proper alignment of objects of any type. If size Is 0,malloc An item of length 0 is allocated in the heap and a valid pointer to the item is returned. Always check malloc Even if the requested amount of memory is small


#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
int main()
{
	//Open up 10 integer spaces
	int* p = (int*)malloc(4000000000000000);
	if (NULL == p)
	{
		printf("%s\n", strerror(errno));//Not enough space
	}
	return 0;
}

free

void free( void *memblock );
Previously allocated memory block to be freed
    If the space opened up is not released,Then others can't use it
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
int main()
{
	//Open up 10 integer spaces
	int* p = (int*)malloc(40);//malloc returns void*
	if (NULL == p)
	{
		printf("%s\n", strerror(errno));//Not enough space
	}

	//use
	if (p)//You need to check the validity of the pointer or add return to the if above;
	{
		for (size_t i = 0; i < 10; i++)
		{
			*(p + i) = i;
		}
		for (size_t i = 0; i < 10; i++)
		{
			printf("%d ", p[i]);//0 1 2 3 4 5 6 7 8 9
		}
	}

	//release
	free(p);//After the space is released, P saves the same address, so p becomes a wild pointer
	p = NULL;//Set p to NULL after all spaces pointed to by P are released
	return 0;
}

calloc

void *calloc( size_t num, size_t size );

Allocates an array in memory with elements initialized to 0.
    
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>

int main()
{
	//Open up 10 integer spaces
	int* p = (int*)calloc(10,sizeof(int));//calloc also returns void*
	if (NULL == p)
	{
		printf("%s\n", strerror(errno));//Not enough space
	}

	//use
	if (p)
	{
		/*for (size_t i = 0; i < 10; i++)
		{
			*(p + i) = i;
		}*/
		for (size_t i = 0; i < 10; i++)
		{
			printf("%d ", p[i]);//0 0 0 0 0 0 0 0 0 0
		}
	}

	//release
	free(p);//After the space is released, P saves the same address, so p becomes a wild pointer
	p = NULL;//Set p to NULL after all spaces pointed to by P are released
	return 0;
}

realloc

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

Reallocate memory blocks.

1.Can open up space
2.You can also adjust the space
    Pointer to a memory block previously allocated with malloc, calloc or realloc.Alternatively, this can be a null pointer, in which case a new block is allocated (as if malloc was called).
     
        
realloc If it is found that the original space is not expanded enough,Will find a new space to open up,And take down the value of the original space,And free up the original space
 If realloc Adjustment failed,Return null pointer
 Never write that:p = (int*)realloc(p, 80);
This may cause the original memory block to leak
    Just write it like this
    int* ptr = (int*)realloc(p, 80);
	if (NULL != ptr)
	{
		p = ptr;
	}

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

int main()
{
	//Open up 10 integer spaces
	int* p = (int*)calloc(10, sizeof(int));//calloc also returns void*
	if (NULL == p)
	{
		printf("%s\n", strerror(errno));//Not enough space
	}

	//use
	if (p)
	{
		for (size_t i = 0; i < 10; i++)
		{
			printf("%d ", p[i]);//0 0 0 0 0 0 0 0 0 0
		}
	}
	//Insufficient space, need to expand capacity
	int* ptr = (int*)realloc(p, 80);
	if (NULL != ptr)
	{
		p = ptr;
	}

	//release
	free(p);//After the space is released, P saves the same address, so p becomes a wild pointer
	p = NULL;//Set p to NULL after all spaces pointed to by P are released
	return 0;
}
r
//Open up 10 integer spaces
	int* p = (int*)realloc(NULL, 40);//If realloc passes NULL, it is equivalent to malloc
	if (NULL == p)
	{
		printf("%s\n", strerror(errno));//Not enough space
	}

2. Common errors

Dereference to NULL pointer

#include<stdio.h>
#include<limits.h>
#include<stdlib.h>
int main()
{
	int* p = (int*)malloc(INT_MAX);//#define INT_MAX       2147483647
	int i = 0;
	for ( i = 0; i < 10; i++)
	{
		*(p + i) = i;
	}
	return 0;
}
#include<stdio.h>
#include<limits.h>
#include<stdlib.h>
#include<assert.h>
int main()
{
	int* p = (int*)malloc(INT_MAX);//#define INT_MAX       2147483647
	assert(p);//Assertion failed: p, if the development fails, a null pointer is returned
	if (p)
	{
		int i = 0;
		for (i = 0; i < 10; i++)
		{
			*(p + i) = i;
		}
	}
	return 0;
}
Or modify it like this:
if (p == NULL)
{
    printf("%s\n", strerror(errno));
    return 0;
}

//use
int i = 0;
for (i = 0; i < 10; i++)
{
    *(p + i) = i;
}

Cross border access to dynamic open space

#include<errno.h>
#include<string.h>
#include<stdio.h>
#include<stdlib.h>
int main()
{
	char* p = (char*)malloc(10 * sizeof(char));
	if (p == NULL)
	{
		printf("%s\n", strerror(errno));
		return 0;
	}

	//use
	for (size_t i = 0; i <= 10; i++)
	{
		*(p + i) = 'a' + 1;//e
	}

	//release
	free(p);
	p = NULL;

	return 0;
}

The free function is used for non dynamic memory

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

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 starting position of dynamic memory. If p cannot find the starting position, there is a risk of memory leakage
    //err
}

Multiple releases of the same dynamic memory

void test()
{
    int *p = (int *)malloc(100);
    free(p);
    free(p);//Repeated release procedure will jam
}
void test()
{
    int *p = (int *)malloc(100);
    free(p);
    p = NULL;
    free(p);//It's OK to release the free null pointer repeatedly, but it's better not to repeat free
}

Dynamic memory forgetting to release (memory leak)

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

    //If you forget to release, there will be a memory leak. Others can't use that space
}
int main()
{
    test();
    while(1);
}
//Who applies for release

3. Written test questions

1.

void GetMemory(char *p) 
{
    p = (char *)malloc(100);
}
void Test(void) 
{
    char *str = NULL;
    GetMemory(str);//Value passing call
    strcpy(str, "hello world");//str is still a null pointer. At this time, it is illegal to access memory, and the program will crash
    printf(str);//It's OK to write like this
}
int main()
{
    Test();
    return 0;
}
//Running result: the program crashed and there was nothing
char* p = "hello world";//Actually, it's the first address
printf("hello world");
printf(p);//It's the first address. It can be written like this
Modify: address calling is enough
void GetMemory(char** p)
{
    *p = (char*)malloc(100);
}
void Test(void)
{
    char* str = NULL;
    GetMemory(&str);
    strcpy(str, "hello world");
    printf(str);//It's OK to write like this

    //release
    free(str);
    str = NULL;
}
int main()
{
    Test();//hello world
    return 0;
}

char* GetMemory(char* p)
{
    p = (char*)malloc(100);//Dynamically requested space will not be destroyed
    return p;
}
void Test(void)
{
    char* str = NULL;
    str = GetMemory(str);
    strcpy(str, "hello world");
    printf(str);//It's OK to write like this

    //release
    free(str);
    str = NULL;
}
int main()
{
    Test();//hello world
    return 0;
}
//No address, return pointer is OK

"be careful: p Although it will be destroyed, it will be destroyed before p Put it in the register"
    
    The space requested by the dynamic memory function is in the heap area and will not be destroyed like local variables

2.

//Return stack space address problem
//Note that it is different from the return variable itself
char *GetMemory(void) 
{
    char p[] = "hello world";//Local variables are destroyed
    return p; //At the end of the function, p will be destroyed. p is the address. The address can be returned, but the space pointed to by the address has been destroyed and is useless
}
void Test(void) 
{
    char *str = NULL;
    str = GetMemory();
    printf(str);//Random value, perm
}
int main()
{
    Test();//
    return 0;
}


int test()
{
    int a = 10;
    return a;//The return variable itself is OK, because when returning, the value of a will be put into the register, and then the value of the register will be returned to m
}
int main()
{
    int m = test();
    printf("%d\n",m);
    return 0;
}

int* test()
{
	int a = 10;
	return &a;
}
int main()
{
	int* p = test();
	printf("%d\n", *p);//Printing out 10 is a fluke, random value
	return 0;
}
int* test()
{
	int a = 10;
	return &a;
}
int main()
{
	int* p = test();
	printf("hehe\n");//At this time, the space applied for this sentence printf will cover the original space of a
	printf("%d\n", *p);//Another random value is printed out
	return 0;
}

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

    //Forget free
    //The following should be added:
    free(str);
    str = NULL;
}

4.

void Test(void) 
{
    char *str = (char *) malloc(100);
    strcpy(str, "hello");
    free(str);
    if(str != NULL)
    {
        strcpy(str, "world");//The space pointed to by str has been released and cannot be used again, resulting in illegal access to memory
        //Therefore, str should be set to NULL in time
        printf(str);
    }
}

4. Memory development of C / C + +

4. Memory development of C / C + + program

The constant string is placed in the code snippet
From kernel space to code segment, it is from high address to low address

  1. Stack: when executing a function, the storage units of local variables in the function can be created on the stack, and these storage units are automatically released at the end of function execution. The stack memory allocation operation 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 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 snippet: the binary code that stores the function body (class member function and global function).

5. Flexible array

In C99, the last element in the structure is allowed to be an array of unknown size, which is called "flexible array" member.

//Two ways to write flexible arrays
struct S1
{
	int n;
	int arr[0];//Size not specified
};
struct S2
{
	int n;
	int arr[];//Size not specified
};

int main()
{
	printf("%d\n", sizeof(struct S1));//4
	printf("%d\n", sizeof(struct S2));//4
	return 0;
}

characteristic

  • 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.
  • A structure containing flexible array members, and the allocated memory should be greater than the size of the structure to accommodate the expected size of the flexible array.
struct S2
{
	int n;
	int arr[];//Size not specified
};

int main()
{
	printf("%d\n", sizeof(struct S1));//4
	printf("%d\n", sizeof(struct S2));//4
	struct S2* ps = (struct S2*)malloc(sizeof(struct S2) + 40);
	ps->n = 100;
	for (size_t i = 0; i < 10; i++)
	{
		ps->arr[i] = i;
	}
	//...
	//increase capacity
	struct S2* ptr = (struct S2*)realloc(ps, sizeof(struct S2) + 80);
	if (ptr == NULL)
	{
		return 0;
	}
	else
	{
		ps = ptr;
	}

	//Continue to use

	//Only one release is required
	free(ps);
	ps = NULL;
	return 0;
}

contrast:

struct S
{
	int n;
	int* arr;
};

int main()
{
	struct S* ps = (struct S*)malloc(sizeof(struct S));
	ps->n = 100;
	ps->arr = (int*)malloc(40);//It's all on the pile
	//use
	//increase capacity

	//2 releases
	free(ps->arr);
	ps->arr = NULL;
	free(ps);
	ps = NULL;

	return 0;
}

What if there are n more variables in front of the flexible array? Will their address space be continuous? Not necessarily, so flexible arrays are still necessary

Moreover, the number of flexible array development and release is less, and it is not easy to make mistakes
The more development times, the more memory fragments will be generated

And continuous memory is conducive to improve access speed

6. Address book

test.c

#define _CRT_SECURE_NO_WARNINGS 1
//1. Store information of 1000 people
//2. Relevant information of the person, name, age, telephone number, address and gender
//3. Addition, deletion, query and modification
//4. Sorting

#include"contact.h"
enum Option
{
	EXIT,
	ADD,
	DEL,
	SEARCH,
	MODIFY,
	SORT,
	SHOW,
	CLS
};
void menu()
{
	printf("***************************************\n");
	printf("********     1.add      2.del     *****\n");
	printf("********     3.search   4.modify  *****\n");
	printf("********     5.sort     6.show    *****\n");
	printf("********     7.cls      0.exit    *****\n");
	printf("***************************************\n");
}

int main()
{
	int input = 0;
	static Contact con;//mail list
	//Initialize address book
	InitContact(&con);
	do
	{
		menu();
		printf("Please select:>");
		scanf("%d", &input);
		switch (input)
		{
		case ADD:
			AddContact(&con);
			break;
		case DEL:
			DeleteContact(&con);
			break;
		case SEARCH:
			SearchContact(&con);
			break;
		case MODIFY:
			ModifyContact(&con);
			break;
		case SORT:
			SortContact(&con);
			break;
		case SHOW:
			ShowContact(&con);
			break;
		case CLS:
			ClsContact();
			break;
		case EXIT:
			//Recycle address book
			DestroyContact(&con);
			break;
			printf("Exit address book\n");
		default:
			printf("Selection error\n");
			break;
		}
	}while(input);
	return 0;
}

contact.h

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<memory.h>
#include<assert.h>
#include<string.h>
#include<stdlib.h>
#include<errno.h>
#include<Windows.h>

#define MAX 1000
#define NAME_MAX 20
#define SEX_MAX 5
#define ADDR_MAX 30
#define TELE_MAX 12
#define DEFAULT_SZ 3 / / the number of people stored by default
typedef struct Peoinfo
{
	char name[NAME_MAX];
	int age;
	char sex[SEX_MAX];
	char addr[ADDR_MAX];
	char tele[TELE_MAX];
}Peoinfo;

//Structure of address book
typedef struct Contact
{
	//Peoinfo data[MAX];
	Peoinfo* data;
	int sz;//Number of valid messages in the address book
	int capacity;//Record the maximum capacity of the current address book
}Contact;

//Initialize address book
void InitContact(Contact* pc);
//Destroy address book
void DestroyContact(Contact* pc);
//Add contact information
void AddContact(Contact* pc);
//Show contacts
void ShowContact(const Contact* pc);
//Delete address book
void DeleteContact(Contact* pc);
//find contact 
void SearchContact(Contact* pc);
//Modify contact
void ModifyContact(Contact* pc);
//Sort contacts
void SortContact(Contact* pc);
//Clear screen
void ClsContact();

contact.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"contact.h"

void InitContact(Contact* pc)
{
	assert(pc);
	pc->sz = 0;
	//data is a continuous space
	//memset(pc->data, 0, sizeof(pc->data));
	Peoinfo* tmp = (Peoinfo*)malloc(DEFAULT_SZ * sizeof(Peoinfo));
	if (NULL != tmp)
	{
		pc->data = tmp;
	}
	else
	{
		printf("InitContact()::%s\n", strerror(errno));
		return;
	}
	pc->capacity = DEFAULT_SZ;
}

void DestroyContact(Contact* pc)
{
	assert(pc);
	free(pc->data);
	pc->data = NULL;
	pc->sz = 0;
	pc->capacity = 0;
}



void check_capacity(Contact* pc)
{
	assert(pc);
	if (pc->sz == pc->capacity)
	{
		//Capacity expansion
		Peoinfo* tmp = (Peoinfo*)realloc(pc->data, (pc->capacity + 2) * sizeof(Peoinfo));
		if (NULL != tmp)
		{
			pc->data = tmp;
			pc->capacity += 2;
			printf("Successful expansion\n");
		}
		else
		{
			printf("Check_Capacity()::%s\n", strerror(errno));
		}
	}
}
void AddContact(Contact* pc)
{
	assert(pc);
	
	//Check whether the capacity is expanded
	check_capacity(pc);

	//Enter contact
	printf("Please enter your name:>\n");
	scanf("%s", pc->data[pc->sz].name);
	printf("Please enter age:>\n");
	scanf("%d", &(pc->data[pc->sz].age));
	printf("Please enter gender:>\n");
	scanf("%s", pc->data[pc->sz].sex);
	printf("Please enter the phone number:>\n");
	scanf("%s", pc->data[pc->sz].tele);
	printf("Please enter the address:>\n");
	scanf("%s", pc->data[pc->sz].addr);

	pc->sz++;
	printf("Contact added successfully\n");
	Sleep(1000);
	system("cls");
}

void ShowContact(const Contact* pc)
{
	assert(pc);
	int i = 0;
	//A minus sign indicates left alignment
	printf("%-20s\t%-5s\t%-5s\t%-13s\t%-20s\n", "name", "Age", "Gender", "Telephone", "address");
	for (i = 0; i < pc->sz; i++)
	{
		printf("%-20s\t%-5d\t%-5s\t%-13s\t%-20s\n",
			pc->data[i].name, pc->data[i].age,
			pc->data[i].sex, pc->data[i].tele,
			pc->data[i].addr);
	}
	printf("Display successful\n");
}

int FindByName(const Contact* pc, char name[])
{
	int i = 0;
	for ( i = 0; i < pc->sz; i++)
	{
		if (strcmp(pc->data[i].name,name) == 0)
		{
			return i;
		}
	}
	return -1;//can't find
}

void DeleteContact(Contact* pc)
{
	char name[NAME_MAX] = { 0 };
	assert(pc);
	if (pc->sz == 0)
	{
		printf("Address book is empty,Cannot delete\n");
		return;
	}
	printf("Please enter the name of the person to delete:>\n");
	scanf("%s", name);
	//Find the specified contact
	int pos = FindByName(pc, name);//subscript
	if (pos == -1)
	{
		printf("The person to delete does not exist\n");
		return;
	}
	else
	{
		//delete
		int j = 0;
		for ( j = pos; j < pc->sz-1; j++)
			//sz-1 if sz is 999, the biggest access is 998998 + 1 without crossing the boundary
		{
			pc->data[j] = pc->data[j + 1];
		}
		pc->sz--;
		printf("Delete specified contact succeeded\n");
	}
}

void SearchContact(Contact* pc)
{
	char name[NAME_MAX] = { 0 };
	assert(pc);
	if (pc->sz == 0)
	{
		printf("Address book is empty,Unable to find\n");
		return;
	}
	printf("Please enter the name of the person you want to find:>\n");
	scanf("%s", name);
	//Find the specified contact
	int pos = FindByName(pc, name);//subscript
	if (pos == -1)
	{
		printf("The person you are looking for does not exist\n");
		return;
	}
	else
	{
		//A minus sign indicates left alignment
		printf("%-20s\t%-5s\t%-5s\t%-13s\t%-20s\n", "name", "Age", "Gender", "Telephone", "address");
		printf("%-20s\t%-5d\t%-5s\t%-13s\t%-20s\n",
				pc->data[pos].name, pc->data[pos].age,
				pc->data[pos].sex, pc->data[pos].tele,
				pc->data[pos].addr);
		
	}
}


void ModifyContact(Contact* pc)
{
	char name[NAME_MAX] = { 0 };
	assert(pc);
	if (pc->sz == 0)
	{
		printf("Address book is empty,Cannot modify\n");
		return;
	}
	printf("Please enter the name of the person to modify:>\n");
	scanf("%s", name);
	//Find the specified contact
	int pos = FindByName(pc, name);//subscript
	if (pos == -1)
	{
		printf("The person to modify does not exist\n");
		return;
	}
	else
	{
		//Modify contact information
		printf("Please enter your name:>\n");
		scanf("%s", pc->data[pos].name);
		printf("Please enter age:>\n");
		scanf("%d", &(pc->data[pos].age));
		printf("Please enter gender:>\n");
		scanf("%s", pc->data[pos].sex);
		printf("Please enter the phone number:>\n");
		scanf("%s", pc->data[pos].tele);
		printf("Please enter the address:>\n");
		scanf("%s", pc->data[pos].addr);

		printf("Contact modified successfully\n");
	}
}
int cmp_con_by_age(const void* e1, const void* e2)
{
	return (((Peoinfo*)e1)->age - ((Peoinfo*)e2)->age);
}

void SortContact(Contact* pc)
{
	assert(pc);
	if (pc->sz == 0)
	{
		printf("Address book is empty,Unable to sort\n");
		return;
	}
	qsort(pc->data, pc->sz, sizeof(Peoinfo), cmp_con_by_age);
	printf("Sorting complete:\n");
	ShowContact(pc);
}



void ClsContact()
{
	Sleep(1000);
	system("cls");
}

Keywords: C C++

Added by flyhoney on Thu, 03 Feb 2022 07:53:45 +0200