S1E48:内存池 课后作业

测试题:
0. 内存碎片和内存泄漏的区别是什么呢?

答:内存碎片(释放了的空间,但是系统不会再利用这片内存了);内存泄漏(内存超限,数据超出了定义的内存范围)(错误

答案:内存碎片和内存泄露是两个不同的概念,它们都会对程序的性能和稳定性产生负面影响。
内存泄露是指程序在分配了内存后,忘记或无法释放,导致这部分内存无法再次使用;
而内存碎片是由于频繁地分配和释放不同大小的内存块,导致内存中留下许多小的、不连续的内存块,降低了内存的利用效率。


1. 请使用简单的代码,举一个可能导致内存碎片的例子?

答:

struct Node *node = (struct Node *)malloc(sizeof(struct Node));

free(node);

答案:在以下的代码中,我们频繁地进行小块内存的分配和释放,这可能会导致内存碎片。

解析:内存碎片问题在一段简单的代码中可能不容易看出,因为它依赖于内存分配器的具体行为以及程序的运行时间。l
在实际的大型程序中,频繁的小块内存分配和释放可能会导致明显的性能问题。

#include <stdio.h>
#include <stdlib.h>
    
void memory_fragment() 
{
    for (int i = 0; i < 1000000; ++i)
    {
        // 分配10字节的内存
        char *buffer = malloc(10);
        if (buffer == NULL) 
        {
            printf("内存分配失败。\n");
            return;
        }
    
        // 使用这个缓冲区做一些操作...
    
        // 释放内存
        free(buffer);
    }
}

int main() 
{
    memory_fragment();
    return 0;
}


2. 请使用简单的代码,举一个可能导致内存泄漏的例子?

答:错误?

int p[10] = {0,1,2,3,4,5,6,7,8,9,10,11};

答案:在以下的代码中,我们创建了一个动态数组,但在函数结束时忘记释放内存,这就导致了内存泄漏。

#include <stdio.h>
#include <stdlib.h>
    
void memory_leak() 
{
    // 使用malloc动态分配100个整数的内存
    int *array = malloc(100 * sizeof(int));
    if (array == NULL) 
    {
        printf("内存分配失败。\n");
        return;
    }
    
    // 使用这个数组做一些操作...
    
    // 忘记释放内存,导致内存泄漏
    // free(array);
}
    
int main() 
{
    memory_leak();
    return 0;
}


{&eDjn`9Mk;Z)H'a52RQr
3. 链表和数组,哪一个结构更容易造成内存碎片?

答:链表,因为链表需要手动申请内存,再通过free释放

答案:链表。
解析:链表和数组都是常用的数据结构,但它们对内存的使用方式和可能造成的内存碎片情况不同。
链表中的每个节点都需要单独分配内存,并且这些节点在内存中的位置可能是分散的。
因此,链表在频繁插入和删除节点时可能会产生许多小的内存碎片。
数组相比之下,数组通常在连续的内存地址空间中分配,因此不太可能


4. 内存池为什么可以解决内存碎片的问题,它的设计思路是什么?

答:内存池就是链表

答案:内存池的设计思路是预先申请一大块连续的内存空间,然后按需从这个内存池中分配内存给程序。
当程序不再需要这些内存时,不立即释放这些内存,而是将这些内存块返回到内存池中,以供以后使用。
这样,就避免了频繁地申请和释放内存,从而减少了内存碎片的产生。


5. 内存池的缺点是什么?

答:使用麻烦

答:虽然内存池可以解决内存碎片问题,但它也有一些缺点。
例如,内存池需要预先申请一大块内存,这可能会占用大量的内存资源;
此外,如果程序的内存需求超过了内存池的大小,就需要重新分配内存池,这也会产生一定的开销。

动动手:
0. 模仿小甲鱼在课堂中实现的通信录程序(内存池版),现在让我们开发一个使用内存池的待办事项程序。
主要功能包括:
        添加待办事项 - 用户可以添加一个新的待办事项到应用中。每个待办事项都有一个名称和描述。如果内存池中有空闲的待办事项,则复用它;如果没有,则为新的待办事项分配内存。
        修改待办事项描述 - 用户可以修改现有的待办事项的描述。他们需要输入待办事项名称,然后输入新的描述。如果找不到待办事项,程序会显示错误信息。
        删除待办事项 - 用户可以删除一个待办事项。他们输入待办事项的名称,然后应用会在列表中查找并删除它。如果找不到待办事项,程序会显示错误信息。删除的待办事项会被放入内存池以便以后复用。
        查看所有待办事项 - 用户可以查看所有的待办事项。程序会遍历待办事项列表,显示每个待办事项的名称和描述。
        退出程序 - 用户可以选择退出程序。退出前,程序会释放待办事项列表和内存池中所有待办事项的内存。

答:

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

struct Node
{
	char name[32];
	char number[32];
	struct Node *next;
};


#define MAX 1024  //定义内存池最大大小 

struct Node *pool = NULL;  //内存池指针 
int count;





void addPerson(struct Node **head)
{
	struct Node *cur = NULL;
	struct Node *temp = NULL;
	
	if(pool != NULL)
	{
		cur = pool;
		pool = pool->next;
		count--;
	
	} 
	else
	{
		cur = (struct Node *)malloc(sizeof(struct Node));
		if(cur == NULL)
		{
			printf("内存分配失败!\r\n");
			exit(1); 	
		}	
		
	}
	
	printf("请输入联系人:");
	scanf("%s",cur->name);
	printf("请输入电话:");
	scanf("%s",cur->number);	
	
	if(*head != NULL)
	{
		temp = *head;
		*head = cur;
		cur->next= temp; 
	}
	else
	{
		*head = cur;
		cur->next=NULL;		
	}
}


struct Node *findPerson(struct Node *head)
{
	char nameinput[32] = {0}; 
	struct Node *temp = head;
	printf("请输入联系人:");
	scanf("%s",nameinput);

	while(temp != NULL)
	{
			if(!strcmp(temp->name,nameinput))
			{
				printf("电话:%s\n",temp->number); 
				break;
			}	
		temp = temp->next; 
	}
	return temp;
} 


void changePerson(struct Node *head)
{
	struct Node *temp = NULL; 
	char nameinput[32]={0};
	
	temp = findPerson(head);	
	if(temp == NULL)
	{
		printf("找不到该联系人\r\n");
	}
	else
	{
		printf("请输入新的联系电话:");
		scanf("%s",temp->number);
	}
}


void delPerson(struct Node **head)
{
	struct Node *pre = NULL;
	struct Node *temp = NULL;
	struct Node *cur = NULL; 
	
	char inputname[32]={0};
	
	cur = findPerson(*head);
		
	if(cur == NULL)
	{
		printf("没有该联系人\r\n");
	}
		
	else
	{
		temp = *head;
		while(temp != NULL && temp != cur)
		{
			pre = temp;
			temp = temp->next;
		}
		
		//删除待办事项,将删除的待办事项放入内存池 
		if(pre == NULL)
		{
			*head = cur->next;
		}
		else{
			
			pre->next= cur->next;
		}
		
		if(count < MAX)
		{
			temp = pool;
			pool = cur;
			cur->next = temp;
			count++; 
		} 
		else
		{
			free(cur);
		}
	} 
}


//显示所有联系人 
void displayPerson(struct Node *head)
{
	struct Node *temp = head;
	while(temp != NULL)
	{
		printf("联系人:%s",temp->name);
		printf("电话%s",temp->number);
		temp = temp->next;
	}	
}


//释放内存 
void releaseContacts(struct Node **head)
{
	struct Node *temp = NULL;
	while(*head != NULL)
	{
		temp = *head;
		*head = (*head)->next;
		free(temp);
		
	}
}

void releasePool(void)
{
	struct Node *cur;
	
	while(cur != NULL)
	{
		cur = pool;
		pool = pool->next;
		free(cur);	
	}
}




int main()
{
	int input = 0;
	printf("|  欢迎使用通讯录管理程序  |\r\n");
	printf("|---  1:插入新的联系人  ---|\r\n");	
	printf("|---  2:查找已有联系人  ---|\r\n");
	printf("|---  3:更改已有联系人  ---|\r\n");
	printf("|---  4:删除已有联系人  ---|\r\n");
	printf("|---  5:显示已有联系人  ---|\r\n");
	printf("|---  6:退出通讯录程序  ---|\r\n");
	
	
	struct Node *person = NULL;
	while(1)
	{
		printf("请输入指令代码:");
		scanf("%d",&input);
		switch(input){
			case 1:
				addPerson(&person);
				break;
			case 2:
				findPerson(person);
				break;	
			case 3:
				changePerson(person);
				break;
			case 4:
				delPerson(&person);
				break;
			case 5:
				displayPerson(person);
				break;
			case 6:
				releaseContacts(&person);
				releasePool();
				break;		
			default:
				break;
		}
		printf("\n");	
	}
		
} 

答案:

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

#define MAX 1024  // 定义内存池最大大小

// 待办事项结构体
struct Todo {
    char name[40];  // 待办事项名称
    char description[100];  // 待办事项描述
    struct Todo *next;  // 指向下一个待办事项的指针
};

struct Todo *pool = NULL;  // 内存池指针
int count;  // 记录内存池中待办事项的数量

// 函数声明
void getInput(struct Todo *todo);
void printTodo(struct Todo *todo);
void addTodo(struct Todo **todos);
void changeTodo(struct Todo *todos);
void delTodo(struct Todo **todos);
struct Todo *findTodo(struct Todo *todos);
void displayTodos(struct Todo *todos);
void releaseTodos(struct Todo **todos);
void releasePool(void);

// 获取待办事项的输入信息
void getInput(struct Todo *todo) {
    printf("请输入待办事项名称:");
    scanf("%s", todo->name);
    printf("请输入待办事项描述:");
    scanf("%s", todo->description);
}

// 添加新的待办事项
void addTodo(struct Todo **todos) {
    struct Todo *todo;
    struct Todo *temp;

    // 如果内存池中有空闲的待办事项,我们复用它而不是分配新的内存
    if (pool != NULL) {
        todo = pool;
        pool = pool->next;
        count--;
    } else {
        // 如果内存池为空,我们分配新的内存
        todo = (struct Todo *)malloc(sizeof(struct Todo));
        if (todo == NULL) {
            printf("内存分配失败!\n");
            exit(1);
        }
    }

    getInput(todo);

    // 如果待办事项列表不为空,我们将新的待办事项添加到列表的头部
    if (*todos != NULL) {
        temp = *todos;
        *todos = todo;
        todo->next = temp;
    } else {
        // 如果待办事项列表为空,我们设置新的待办事项为列表的唯一节点
        *todos = todo;
        todo->next = NULL;
    }
}

// 打印待办事项的详细信息
void printTodo(struct Todo *todo) {
    printf("待办事项:%s\n", todo->name);
    printf("描述:%s\n", todo->description);
}

// 查找特定的待办事项
struct Todo *findTodo(struct Todo *todos) {
    struct Todo *current;
    char input[40];

    printf("请输入待办事项名称:");
    scanf("%s", input);

    current = todos;
    // 遍历待办事项列表,寻找与输入名称匹配的待办事项
    while (current != NULL && strcmp(current->name, input)) {
        current = current->next;
    }

    return current;
}

// 修改待办事项的描述
void changeTodo(struct Todo *todos) {
    struct Todo *todo;

    todo = findTodo(todos);
    if (todo == NULL) {
        printf("找不到该待办事项!\n");
    } else {
        printf("请输入新的待办事项描述:");
        scanf("%s", todo->description);
    }
}

// 删除特定的待办事项
void delTodo(struct Todo **todos) {
    struct Todo *temp;
    struct Todo *todo;
    struct Todo *current;
    struct Todo *previous;

    todo = findTodo(*todos);
    if (todo == NULL) {
        printf("找不到该待办事项!\n");
    } else {
        current = *todos;
        previous = NULL;

        // 遍历待办事项列表,找到待删除的待办事项
        while (current != NULL && current != todo) {
            previous = current;
            current = current->next;
        }

        // 删除待办事项,并重新连接待办事项列表
        if (previous == NULL) {
            *todos = current->next;
        } else {
            previous->next = current->next;
        }

        // 如果内存池未满,我们将删除的待办事项放入内存池
        if (count < MAX) {
           temp = pool;
            pool = current;
            current->next = temp;
            count++;
        } else {
            // 如果内存池已满,我们释放待办事项的内存
            free(current);
        }
    }
}

// 显示所有的待办事项
void displayTodos(struct Todo *todos) {
    struct Todo *current;

    current = todos;
    // 遍历待办事项列表,并打印每个待办事项的详细信息
    while (current != NULL) {
        printTodo(current);
        current = current->next;
    }
}

// 释放所有待办事项的内存
void releaseTodos(struct Todo **todos) {
    struct Todo *current;

    current = *todos;
    // 遍历待办事项列表,并释放每个待办事项的内存
    while (current != NULL) {
        *todos = current->next;
        free(current);
        current = *todos;
    }
}

// 释放内存池中所有待办事项的内存
void releasePool(void) {
    struct Todo *current;

    current = pool;
    // 遍历内存池,并释放每个待办事项的内存
    while (current != NULL) {
        pool = current->next;
        free(current);
        current = pool;
    }
}

int main(void) {
    struct Todo *todos = NULL;
    int option;

    while (1) {
        printf("1. 添加待办事项\n");
        printf("2. 修改待办事项描述\n");
        printf("3. 删除待办事项\n");
        printf("4. 查看所有待办事项\n");
        printf("5. 退出\n");
        printf("请选择操作:");
        scanf("%d", &option);

        switch (option) {
            case 1:
                addTodo(&todos);
                break;
            case 2:
                changeTodo(todos);
                break;
            case 3:
                delTodo(&todos);
                break;
            case 4:
                displayTodos(todos);
                break;
            case 5:
                releaseTodos(&todos);
                releasePool();
                return 0;
            default:
                printf("无效的选项!\n");
        }
    }

    return 0;
}

1. 兵不厌诈,熟能生巧!还是一样的套路,让我们来设计一个学生信息管理系统。
主要功能包括:
        插入新的学生信息 - 它会提示用户输入学生的学号,姓名和专业,并将输入的信息存储在一个新创建的 Student 结构体实例中。这个新创建的学生信息会被添加到链表的开始。
        查找已有学生信息 - 该功能会提示用户输入一个学生的学号,然后遍历链表查找该学号对应的学生。如果找到,它将显示该学生的信息,否则,它将打印一个消息说找不到该学生。
        更改已有学生信息 - 这个功能也会提示用户输入一个学生的学号,然后查找该学生。如果找到,它将提示用户输入新的专业,并更新该学生的信息。
        删除已有学生信息 - 用户输入一个学号,程序会查找并删除对应的学生信息。如果找到并删除了该学生,它将把该学生的内存空间添加到一个内存池中,以便再次使用。如果内存池已满,它将释放该学生的内存空间。
        显示当前所有学生信息 - 它将遍历链表,打印出所有学生的信息。
        退出学生信息管理程序 - 当用户选择这个选项时,程序将释放所有的学生信息和内存池中的内存空间,并退出程序。

答:

答案:

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

#define MAX 1024  // 定义内存池的最大容量

// 定义一个学生的结构体
struct Student
{
        char id[10];  // 学生的学号
        char name[40];  // 学生的姓名
        char major[20];  // 学生的专业
        struct Student *next;  // 指向链表中下一个学生的指针
};

struct Student *pool = NULL;  // 定义一个全局的内存池
int count;  // 当前内存池中的学生数量

// 获取用户输入的学生信息并保存到给定的学生结构体中
void getInput(struct Student *student);

// 打印给定的学生的信息
void printStudent(struct Student *student);

// 添加一个新的学生到链表
void addStudent(struct Student **students);

// 修改链表中的一个学生的信息
void changeStudent(struct Student *students);

// 从链表中删除一个学生
void delStudent(struct Student **students);

// 根据学号查找链表中的一个学生
struct Student *findStudent(struct Student *students);

// 打印链表中所有学生的信息
void displayStudents(struct Student *students);

// 释放链表中所有学生的内存
void releaseStudents(struct Student **students);

// 释放内存池中所有学生的内存
void releasePool(void);

// ... 此处省略了函数的实现 ...

int main(void)
{
        int code;  // 用户输入的操作代码
        struct Student *students = NULL;  // 链表的头指针
        struct Student *student;  // 临时变量,用于保存查找到的学生

        // 打印欢迎信息和操作菜单
        printf("| 欢迎使用学生信息管理程序 |\n");
        printf("|--- 1:插入新的学生信息 ---|\n");
        printf("|--- 2:查找已有学生信息 ---|\n");
        printf("|--- 3:更改已有学生信息 ---|\n");
        printf("|--- 4:删除已有学生信息 ---|\n");
        printf("|--- 5:显示当前所有学生信息 ---|\n");
        printf("|--- 6:退出学生信息管理程序 ---|\n");

        while (1)  // 主循环,直到用户选择退出
        {
                printf("\n请输入指令代码:");
                scanf("%d", &code);  // 获取用户输入的操作代码

                // 根据用户输入的操作代码执行相应的操作
                switch (code)
                {
                        case 1: addStudent(&students); break;  // 添加新的学生
                        case 2:  // 查找学生
                                student = findStudent(students);
                                if (student == NULL)  // 如果找不到学生,打印错误信息
                                {
                                        printf("找不到该学生!\n");
                                }
                                else  // 如果找到学生,打印其信息
                                {
                                        printStudent(student);
                                }
                                break;
                        case 3: changeStudent(students); break;  // 修改学生信息
                        case 4: delStudent(&students); break;  // 删除学生
                        case 5: displayStudents(students); break;  // 显示所有学生信息
                        case 6: goto END;  // 退出程序
                }
        }

END:
        releaseStudents(&students);  // 释放链表中所有学生的内存
        releasePool();  // 释放内存池中所有学生的内存

        return 0;
}

  • 17
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值