"Little Cat Classroom" Three Rounds 5 - Dynamic Memory Management (Dynamic Memory of Address Book)

Baozi, don’t you like it? Don't you want to comment? Don't you want to collect it?

Finally, follow me, follow me, follow me, you will see more interesting blogs! ! !

Meow meow meow, you are really important to me.

Table of contents

Preface

Causes of dynamic memory

Dynamic memory functions

malloc

free

         calloc

         realloc

Common dynamic memory errors

Dereference operation on NULL pointer

Out-of-bounds access to dynamically opened space

Use free release for non-dynamically allocated memory

Use free to release a part of dynamically allocated memory

Release the same dynamic memory multiple times

Dynamically allocated memory and forgets to release it (memory leak)

To give a few examples, what will be the result of the Test function?

Memory allocation for C/C++ programs

flexible array

Features of flexible arrays:

Advantages of Flexible Arrays

Address book (dynamic memory)

open up space

Initialize address book

Add contacts (may need to be expanded)

Destroy address book

Summarize


Preface

Dynamic memory is not difficult to understand. If it looks good, it should be very good! There are many good videos on Station B. That one may be easier to understand. It should be great if you practice the questions to build up your confidence! Come on, Baozi! Love you! Meow~


Causes of dynamic memory

int val = 20;//在栈空间上开辟四个字节
char arr[10] = {0};//在栈空间上开辟10个字节的连续空间

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

1. The space opening size is fixed.

2. When declaring an array, the length of the array must be specified, and the memory it requires is allocated at compile time.

But the demand for space is not just the above-mentioned situations. Sometimes the amount of space we need can only be known when the program is running, so the method of creating space during compilation of the array is not sufficient. At this time, you can only try dynamic storage development.


Dynamic memory functions

malloc

void* malloc (size_t size);

This function applies for a continuous available space in memory and returns a pointer to this space.

  • If the allocation is successful, a pointer to the allocated space is returned.
  • If the development fails, a NULL pointer will be 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 space to open, and the user must decide by himself when using it.
  • If the size parameter is 0, the behavior of malloc is undefined by the standard and depends on the compiler.

free

void free (void* ptr);

free is specifically used to release and recycle dynamic memory.

The free function is used to release dynamically allocated memory.

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

Give a chestnut:

Both malloc and free are declared in the stdlib.h header file.

#include <stdio.h>
int main()
{
 //代码1
 int num = 0;
 scanf("%d", &num);
 int arr[num] = {0};
 //代码2
 int* ptr = NULL;
 ptr = (int*)malloc(num*sizeof(int));
 if(NULL != ptr)//判断ptr指针是否为空
 {
 int i = 0;
 for(i=0; i<num; i++)
 {
 *(ptr+i) = 0;
 }
 }
 free(ptr);//释放ptr所指向的动态内存
 ptr = NULL;//是否有必要?
 return 0;
}

calloc

void* calloc (size_t num, size_t size);
  • 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.
#include <stdio.h>
#include <stdlib.h>
int main()
{
 int *p = (int*)calloc(10, sizeof(int));
 if(NULL != p)
 {
 //使用空间
 }
 free(p);
 p = NULL;
 return 0;
}


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 use the memory at a reasonable time, we will definitely make flexible adjustments to the memory size. Then the realloc function can adjust the dynamically allocated memory size.
void* realloc (void*ptr, size_t size);
  • ptr is the memory address to be adjusted
  • size new size after adjustment
  • The return value is the starting position of the memory 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.

There are two situations when realloc adjusts the memory space:

Situation 1: There is enough space behind the original space

Situation 2: There is not enough space after the original space

 If there's enough space in the back, it's fine. If there is not enough space, it will find enough space to use, and the address will change.

Case 1

When it is case 1, if you want to expand the memory, just add space directly after the original memory, and the data in the original space will not change.

Case 2

When it is case 2, when there is not enough space after the original space, the expansion method is to find another continuous space of suitable size on the heap space to use. This function returns a new memory address. Due to the above two situations, some attention should be paid to the use of realloc function.

Give a chestnut:

#include <stdio.h>
int main()
{
 int *ptr = (int*)malloc(100);
 if(ptr != NULL)
 {
     //业务处理
 }
 else
 {
     exit(EXIT_FAILURE);    
 }
 //扩展容量
 //代码1
 ptr = (int*)realloc(ptr, 1000);//这样可以吗?(如果申请失败会如何?)
 
 //代码2
 int*p = NULL;
 p = realloc(ptr, 1000);
 if(p != NULL)
 {
 ptr = p;
 }
 //业务处理
 free(ptr);
 return 0;
}

Common dynamic memory errors

Dereference operation on NULL pointer

void test()
{
 int *p = (int *)malloc(INT_MAX/4);
 *p = 20;//如果p的值是NULL,就会有问题
 free(p);
}

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<=10; i++)
 {
 *(p+i) = i;//当i是10的时候越界访问
 }
 free(p);
}

Use free release for non-dynamically allocated memory

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

Use free to release a part of dynamically allocated memory

void test()
{
 int *p = (int *)malloc(100);
 p++;
 free(p);//p不再指向动态内存的起始位置
}

Release the same dynamic memory multiple times

void test()
{
 int *p = (int *)malloc(100);
 free(p);
 free(p);//重复释放
}

Dynamically allocated memory and forgets to release it (memory leak)

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


To give a few examples, what will be the result of 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);
}

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

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

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

Memory allocation for C/C++ programs

Several areas of memory allocation for C/C++ programs:

1. Stack area (stack): When executing a function, the storage units of local variables within the function can be created on the stack, and these storage units are automatically released when the function execution ends. The stack memory allocation operation is built into the processor's instruction set and is very efficient, but the allocated memory capacity is limited. The stack area mainly stores local variables, function parameters, return data, return addresses, etc. allocated for running functions.

2. Heap area (heap): Generally allocated and released by the programmer. If the programmer does not release it, it may be recycled by the OS when the program ends. The allocation method is similar to a linked list.

3. The data segment (static area) stores global variables and static data. It is released by the system after the program ends.

4. Code segment: stores the binary code of the function body (class member function and global function).​ 

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 go out of scope. However, variables modified by static are stored in the data segment (static area). The characteristic of the data segment is that the variables created above are not destroyed until the end of the program.

So the life cycle becomes longer.


flexible array

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

typedef struct st_type
{
 int i;
 int a[];//柔性数组成员
}type_a;

Features of flexible arrays:

  • Flexible array members 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 flex array.
  • The structure containing flexible array members uses the malloc () function to dynamically allocate memory, and the allocated memory should be larger than the size of the structure to accommodate the expected size of the flexible array.
//code1
typedef struct st_type
{
 int i;
 int a[0];//柔性数组成员
}type_a;
printf("%d\n", sizeof(type_a));//输出的是4
//代码1
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);

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

Advantages of Flexible Arrays

The above type_a structure can also be designed as:

//代码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));
//业务处理
for(i=0; i<100; i++)
{
 p->p_a[i] = i;
}
//释放空间
free(p->p_a);
p->p_a = NULL;
free(p);
p = NULL;

The above code 1 and code 2 can accomplish the same function, but the implementation of method 1 has two benefits:

The first benefit is: convenient memory release. If our code is in a function for others to use, you make a secondary memory allocation in it and return the entire 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 to be free, so you cannot expect the user to discover this. Therefore, if we allocate the memory of the structure and the memory required by its members at once, and return a structure pointer to the user, the user can free all the memory once.

The second benefit is: This is beneficial to access speed. Continuous memory is beneficial to improving access speed and also beneficial to reducing memory fragmentation. (Actually, I personally don’t think it’s much higher. Anyway, you can’t run and you have to use offset addition to address)


Address book (dynamic memory)

Address Book Basic Edition(Click to take a look!)

Modified source code

//teat.c
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include"contact.h"
void menu()
{
	printf("*********************************\n");
	printf("*****   1.add     2.del    ******\n");
	printf("*****   3.search  4.modify ******\n");
	printf("*****   5.show    6.sort   ******\n");
	printf("*****   7.dea     0.exit   ******\n");
	printf("*********************************\n");
}
int main()
{
	int input = 0;
	//ͨѶ¼
	PeoInfo  data[1000];
	int sz = 05l;
	Contact con;
	InitContact(&con);
	do
	{
		menu();
		printf("请输入->");
		scanf("%d", &input);
		switch (input)
		{
		case  1:
			AddContact(&con);
			break;
		case  2:
			DelContact(&con);
			break;
		case  3:
			SearchContact(&con);
			break;
		case  4:
			ModifyContact(&con);
			break;
		case  5:
			ShowContact(&con);
			break;
		case  6:
			SortContact(&con);
			break;
		case  7:
			/*DeaContact(&con);*/
			DestoryContact(&con);
			break;
		case  0:
			printf("退出通讯录\n");
			break;
		default:
			printf("选择错误\n");
			break;
		}
	} while (input);
	return 0;
}

//Contact.c
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
#include"contact.h"
//静态
//void InitContact(Contact* pc)
//{
//	assert(pc);
//	pc->sz = 0;
//	memset(pc->data, 0, sizeof(pc->data));
//}
void InitContact(Contact* pc)
{
	assert(pc);
	pc->sz = 0;
	PeoInfo* ptr = (PeoInfo*)calloc(sizeof(PeoInfo), DEFAULT_SZ);
	if (ptr == NULL)
	{
		perror("InitContact::calloc");
		return;
	}
	pc->data = ptr;
	pc->capacity = DEFAULT_SZ;
}
//void AddContact(Contact* pc)
//{
//	assert(pc);
//	if (pc->sz == 1000)
//	{
//		printf("通讯录已满,无法添加\n");
//		return;
//	}
//	//增加一个人的信息
//	printf("请输入名字:>");
//	scanf("%s", pc->data[pc->sz].name);
//	printf("请输入年龄:>");
//	scanf("%d", &(pc->data[pc->sz].age));
//	printf("请输入性别:>");
//	scanf("%s", pc->data[pc->sz].sex);
//	printf("请输入地址:>");
//	scanf("%s", pc->data[pc->sz].addr);
//	printf("请输入电话:>");
//	scanf("%s", pc->data[pc->sz].tele);
//	pc->sz++;
//}
void check_capacity(Contact* pc)
{
	if (pc->sz == pc->capacity)
	{
		//增加容量
		PeoInfo* ptr = (PeoInfo*)realloc(pc->data, (pc->capacity + 2) * sizeof(PeoInfo));
		if (ptr == NULL)
		{
			perror("check_capacity::realloc");
			return;
		}
		pc->data = ptr;
		pc->capacity += INC_SZ;
		printf("增容成功");
	}
}
void AddContact(Contact* pc)
{
	assert(pc);
	check_capacity(pc);
	if (pc->sz == pc->capacity)
	{
		//增加容量

	}
	//增加一个人的信息
	printf("请输入名字:>");
	scanf("%s", pc->data[pc->sz].name);
	printf("请输入年龄:>");
	scanf("%d", &(pc->data[pc->sz].age));
	printf("请输入性别:>");
	scanf("%s", pc->data[pc->sz].sex);
	printf("请输入地址:>");
	scanf("%s", pc->data[pc->sz].addr);
	printf("请输入电话:>");
	scanf("%s", pc->data[pc->sz].tele);
	pc->sz++;
}
void ShowContact(const Contact* pc)
{
	assert(pc);
	int i = 0;
	printf("%-20s\t%-4s\t%-5s\t%-20s\t%-15s\n", "名字", "年龄", "性别", "地址", "电话");
	for (i = 0; i < pc->sz; i++)
	{
		printf("%-20s\t%-4d\t%-5s\t%-20s\t%-15s\n", pc->data[i].name, pc->data[i].age, pc->data[i].sex, pc->data[i].addr, pc->data[i].tele);
	}

}

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


void DelContact(Contact* pc)
{
	assert(pc);
	char name[NAME_MAX] = { 0 };
	if (pc->sz == 0)
	{
		printf("通讯录为空,无法删除");
		return;
	}
	//删除
	//找出要删除的人
	printf("请输入要删除的人的名字;>");
	scanf("%s", name);
	int ret = FindByName(pc, name);
	if (-1 == ret)
	{
		printf("要删除的人不存在\n");
		return;
	}


	int i = 0;
	int del = 0;
	for (i = 0; i < pc->sz; i++)
	{
		if (strcmp(pc->data[i].name, name) == 0)
		{
			del = i;
			break;
		}
	}
	//删除
	for (i = del; i < pc->sz; i++)
	{
		pc->data[i] = pc->data[i + 1];
	}
	pc->sz--;
	printf("删除成功\n");
}

void SearchContact(const Contact* pc)
{
	assert(pc);
	char name[NAME_MAX] = { 0 };
	printf("请输入要查找人的名字:>");
	scanf("%s", name);
	int pos = FindByName(pc, name);
	if (-1 == pos)
	{
		printf("要查找的人不存在\n");
		return;
	}
	//打印信息
	printf("%-20s\t%-4s\t%-5s\t%-20s\t%-15s\n", "名字", "年龄", "性别", "地址", "电话");
	printf("%-20s\t%-4d\t%-5s\t%-20s\t%-15s\n", pc->data[pos].name, pc->data[pos].age, pc->data[pos].sex, pc->data[pos].addr, pc->data[pos].tele);
}

void ModifyContact(Contact* pc)
{
	assert(pc);
	char name[NAME_MAX] = { 0 };
	printf("请输入要删除的人的名字;>");
	scanf("%s", name);
	int pos = FindByName(pc, name);
	if (-1 == pos)
	{
		printf("要删除的人不存在\n");
		return;
	}
	printf("请输入名字:>");
	scanf("%s", pc->data[pos].name);
	printf("请输入年龄:>");
	scanf("%d", &(pc->data[pos].age));
	printf("请输入性别:>");
	scanf("%s", pc->data[pos].sex);
	printf("请输入地址:>");
	scanf("%s", pc->data[pos].addr);
	printf("请输入电话:>");
	scanf("%s", pc->data[pos].tele);
	printf("修改完成\n");
}

//排序函数
		//1.按照姓名进行排序
int Conpare_ByName(const void* e1, const void* e2)
{
	return strcmp(((struct PeoInfo*)e1)->name, ((struct PeoInfo*)e2)->name);
}
//2.按照年龄进行排序
int Conpare_ByAge(const void* e1, const void* e2)
{
	return ((struct PeoInfo*)e1)->age - ((struct PeoInfo*)e2)->age;
}
//3.按照住址进行排序
int Conpare_ByAddress(const void* e1, const void* e2)
{
	return strcmp(((struct PeoInfo*)e1)->addr, ((struct PeoInfo*)e2)->addr);
}
void SortContact(Contact* pc)
{
	assert(pc);

	printf("请选择你想排序的方式:\n");
	printf("1.姓名\n2.年龄\n3.住址\n");
	int input = 0;
	scanf("%d", &input);
	switch (input)
	{
	case 1:
		qsort(pc->data, pc->sz, sizeof(pc->data[0]), Conpare_ByName);
		printf("排序成功\n");
		break;
	case 2:
		qsort(pc->data, pc->sz, sizeof(pc->data[0]), Conpare_ByAge);
		printf("排序成功\n");
		break;
	case 3:
		qsort(pc->data, pc->sz, sizeof(pc->data[0]), Conpare_ByAddress);
		printf("排序成功\n");
		break;
	}
}


//void DeaContact(Contact* pc)
//{
//	assert(pc);
//	memset(pc->data, 0, sizeof(pc->data));
//	pc->sz == 0;
//	printf("清空成功!\n");
//
//}
void DestoryContact(Contact* pc)
{
	free(pc->data);
	pc->data = NULL;
	pc->sz = 0;
	pc = NULL;
}
//Contact.h
#pragma once
#define MAX 1000
#define NAME_MAX 20
#define SEX_MAX 5
#define ADDR_MAX 30
#define TELE_MAX 12

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

#define DEFAULT_SZ 3//默认大小
#define INC_SZ 2

//人的信息
typedef struct PeoInfo
{
	char name[NAME_MAX];
	int age;
	char sex[SEX_MAX];
	char addr[ADDR_MAX];
	char tele[TELE_MAX];
}PeoInfo;



struct contact
{
	struct PeoInfo data[NAME_MAX];
	int sz;//记录当前已经有的元素个数
};
//静态
//typedef struct Contact
//{
//	PeoInfo data[1000];//存放人的信息
//	int sz;//当前已存放信息的个数
//}Contact;
//动态 
typedef struct Contact
{
	PeoInfo* data;//指向存放人的信息
	int sz;//当前已存放信息的个数
	int  capacity;//当前通讯录最大容量
}Contact;
//初始化通讯录
void InitContact(Contact* pc);
//销毁通讯录
void DestoryContact(Contact* pc);
删除所有联系人
//void DeaContact(Contact* pc);
//增加联系人
void AddContact(Contact* pc);
//删除指定联系人
void DelContact(Contact* pc);
//显示通讯录
void ShowContact(const Contact* pc);
//查找指定联系人
void SearchContact(Contact* pc);
//修改指定联系人
void ModifyContact(Contact* pc);
//联系人排序
void SortContact(Contact* pc);
//删除所有联系人

open up space

//Contact.h
//静态
//typedef struct Contact
//{
//	PeoInfo data[1000];//存放人的信息
//	int sz;//当前已存放信息的个数
//}Contact;
//动态 
typedef struct Contact
{
	PeoInfo* data;//指向存放人的信息
	int sz;//当前已存放信息的个数
	int  capacity;//当前通讯录最大容量
}Contact;

Initialize address book

//静态
//Contact.c
//void InitContact(Contact* pc)
//{
//	assert(pc);
//	pc->sz = 0;
//	memset(pc->data, 0, sizeof(pc->data));
//}
void InitContact(Contact* pc)
{
	assert(pc);
	pc->sz = 0;
	PeoInfo* ptr = (PeoInfo*)calloc(sizeof(PeoInfo), DEFAULT_SZ);
	if (ptr == NULL)
	{
		perror("InitContact::calloc");
		return;
	}
	pc->data = ptr;
	pc->capacity = DEFAULT_SZ;
}

Add contacts (may need to be expanded)

//void AddContact(Contact* pc)
//{
//	assert(pc);
//	if (pc->sz == 1000)
//	{
//		printf("通讯录已满,无法添加\n");
//		return;
//	}
//	//增加一个人的信息
//	printf("请输入名字:>");
//	scanf("%s", pc->data[pc->sz].name);
//	printf("请输入年龄:>");
//	scanf("%d", &(pc->data[pc->sz].age));
//	printf("请输入性别:>");
//	scanf("%s", pc->data[pc->sz].sex);
//	printf("请输入地址:>");
//	scanf("%s", pc->data[pc->sz].addr);
//	printf("请输入电话:>");
//	scanf("%s", pc->data[pc->sz].tele);
//	pc->sz++;
//}
void check_capacity(Contact* pc)
{
	if (pc->sz == pc->capacity)
	{
		//增加容量
		PeoInfo*ptr=(PeoInfo*)realloc(pc->data, (pc->capacity + 2) * sizeof(PeoInfo));
		if (ptr == NULL)
		{
			perror("check_capacity::realloc");
			return;
		}
		pc->data = ptr;
		pc->capacity += INC_SZ;
	}
}
void AddContact(Contact* pc)
{
	assert(pc);
	check_capacity(pc);
	if (pc->sz == pc->capacity)
	{
		//增加容量

	}
	//增加一个人的信息
	printf("请输入名字:>");
	scanf("%s", pc->data[pc->sz].name);
	printf("请输入年龄:>");
	scanf("%d", &(pc->data[pc->sz].age));
	printf("请输入性别:>");
	scanf("%s", pc->data[pc->sz].sex);
	printf("请输入地址:>");
	scanf("%s", pc->data[pc->sz].addr);
	printf("请输入电话:>");
	scanf("%s", pc->data[pc->sz].tele);
	pc->sz++;
}

Destroy address book

//Contact.h
//销毁通讯录
void DestoryContact(Contact* pc);
删除所有联系人
//void DeaContact(Contact* pc);
//Contact.c
//void DeaContact(Contact* pc)
//{
//	assert(pc);
//	memset(pc->data, 0, sizeof(pc->data));
//	pc->sz == 0;
//	printf("清空成功!\n");
//
//}
void DestoryContact(Contact* pc)
{
	free(pc->data);
	pc->data = NULL;
	pc->sz = 0;
	pc = NULL;
}

Remember there is also test.c!


Summarize

The recent knowledge is a bit difficult to understand! The more you don’t understand, the more you need to understand it and the more you need to write it in a blog!

Time is a little tight. There are many exams recently. The article is not detailed enough. When I review it in the future, the article will be redone. The framework is complete. I am just afraid that the content is not detailed enough and difficult to understand. I am very sorry. I will optimize it when I review it again in the future. ! terribly sorry.


 Baozi, don’t you like it? Don't you want to comment? Don't you want to collect it?

Finally, follow me, follow me, follow me, you will see more interesting blogs! ! !

Meow meow meow, you are really important to me.​ 

Guess you like

Origin blog.csdn.net/ormstq/article/details/128690858