当前位置: 首页 > news >正文

C语言动态内存管理与文件操作:打造高效通讯录

本篇博客会讲解如何使用C语言实现一个通讯录。实现通讯录的过程中,会大量用到C语言的知识点,包括但不限于:函数、自定义类型、指针、动态内存管理、文件操作,这些知识点在我的其他博客中都有讲解过,欢迎大家阅读,这里就不进行系统的复习了。

先来梳理下需求:

  1. 通讯录能够存储的联系人的信息有:姓名、年龄、性别、电话、住址。
  2. 这个通讯录不能是“静态的”,而应该是“动态的”,也就是说,需要用到动态内存管理的知识。这是因为,静态的通讯录的容量是固定的,空间太大可能浪费,太小了又不够存。
  3. 由于当程序开始运行后,通讯录的数据是存储在内存中的,一旦程序运行结束,执行完main函数的return 0;后,空间就被操作系统回收了,相当于数据就丢了。为了能够实现“永久保存”的效果,我们要在程序退出前,把数据保存到文件中,这又涉及到文件操作的相关知识点。
  4. 类似顺序表这种数据结构的基本操作,通讯录要能做到:增删查改+排序+打印,即增加联系人、删除联系人、查找联系人、修改联系人、排序联系人、打印联系人等等。

下面我们开始吧!
在这里插入图片描述

准备工作

以下是菜单里的一些选项,声明成枚举类型是比较合适的。

// 菜单里的不同选项
enum Option
{EXIT,   // 退出ADD,    // 增加联系人DEL,    // 删除联系人SEARCH, // 查找联系人MODIFY, // 修改联系人SHOW,   // 显示联系人SORT    // 排序
};

由于联系人的姓名、性别、电话和住址都是字符串,要存储在字符数组中,最好先声明它们的容量。

// 各信息的存储容量
#define MAX_NAME 20 // 名字
#define MAX_SEX 5   // 性别
#define MAX_TELE 12 // 电话
#define MAX_ADDR 30 // 住址

我们后面在进行动态内存管理时,需要知道初始的容量和每次扩容的容量,也声明一下:

// 动态内存默认存储的数据
#define DEFAULT_SZ 3
// 若不够存,每次扩容的数量
#define INC_SZ 2

再声明一个结构体,表示一个人的信息,包括姓名、年龄、性别、电话、住址。

// 表示一个人的信息
typedef struct PeoInfo
{char name[MAX_NAME]; // 姓名int age;             // 年龄char sex[MAX_SEX];   // 性别char tele[MAX_TELE]; // 电话char addr[MAX_ADDR]; // 住址
}PeoInfo;

类似数据结构中的“顺序表”的结构,定义一个结构体,用于存储通讯录中的信息,包括一个动态开辟的数组,数组中有效数据的个数,以及数组当前动态开辟的容量。

// 通讯录
typedef struct Contact
{PeoInfo* data; // data指向了存放数据的空间int sz;        // 记录通讯录中的有效信息个数int capacity;  // 通讯录当前的容量
}Contact;

下面我们开始实现程序的主体逻辑。先从主函数写起,把主要的功能都封装成函数:

// 打印菜单
void menu()
{printf("************************************\n");printf("*****    1. add     2. del       ***\n");printf("*****    3. search  4. modify    ***\n");printf("*****    5. show    6. sort      ***\n");printf("*****    0. exit                 ***\n");printf("************************************\n");
}int main()
{int input = 0; // 存储用户输入的数据Contact con; // 通讯录// 初始化通讯录// 加载文件的信息到通讯录中InitContact(&con);do{menu(); // 菜单printf("请选择:>");scanf("%d", &input);switch (input){case ADD: // 添加联系人AddContact(&con);break;case DEL: // 删除联系人DelContact(&con);break;case SEARCH: // 查找指定联系人SearchContact(&con);break;case MODIFY: // 修改指定联系人的信息ModifyContact(&con);break;case SHOW: // 展示联系人信息ShowContact(&con);break;case SORT: // 排序SortContact(&con);break;case EXIT: // 退出通讯录// 保存通讯录到文件中SaveContact(&con);// 销毁通讯录DestroyConact(&con);printf("退出通讯录\n");break;default:printf("选择错误\n");break;}} while (input);return 0;
}

初始化通讯录

先定义一个函数InitContact,它的作用是初始化通讯录。函数的参数是一个指向Contact结构体的指针。函数的具体实现如下:

  1. 函数的第一行使用assert宏检查指针是否有效,如果无效则程序会中止运行。
  2. 接下来,函数使用malloc函数开辟了一块内存空间,用于存储PeoInfo结构体数组。这个数组的大小是DEFAULT_SZ,即默认容量。如果开辟空间失败,则会输出错误信息并返回。
  3. 如果开辟空间成功,则将通讯录的大小sz和容量capacity都设置为DEFAULT_SZ,并调用LoadContact函数将文件中的信息加载到通讯录中。
void InitContact(Contact* pc)
{// 检查指针有效性assert(pc);// 先开辟默认的容量pc->data = (PeoInfo*)malloc(DEFAULT_SZ * sizeof(PeoInfo));// 检查开辟空间是否成功if (pc->data == NULL){// 开辟空间失败printf("通讯录初始化失败:%s\n", strerror(errno));return;}// 开辟空间成功pc->sz = 0;pc->capacity = DEFAULT_SZ;//加载文件的信息到通讯录LoadContact(pc);
}

从文件中加载信息

再定义一个函数LoadContact,用于从文件中读取联系人信息并存储到内存中。函数的参数是一个指向Contact结构体的指针,表示要将读取到的联系人信息存储到哪个数据结构中。函数的具体实现如下:

  1. 首先使用assert函数检查传入的指针是否有效,如果无效则直接返回。
  2. 然后使用fopen函数打开名为"contact.dat"的二进制文件,如果打开失败则说明可能是第一次运行通讯录,没有数据文件,直接返回。
  3. 接着使用一个while循环,每次读取一个PeoInfo结构体大小的数据,即一个联系人的信息,存储到临时变量tmp中。
  4. 调用CheckCapacity函数检查当前动态数组的容量是否足够存储读取到的联系人信息,如果不够则进行扩容。
  5. 将读取到的联系人信息存储到动态数组中,即将tmp变量中的数据存储到data数组的末尾,并将sz变量加一。
  6. 循环结束后,关闭文件并将文件指针置为 NULL。
void LoadContact(Contact* pc)
{// 检查指针有效性assert(pc);// 打开文件FILE* pf = fopen("contact.dat", "rb");// 检查打开文件是否成功if (pf == NULL){// 打开文件失败,可能是第一次运行通讯录,并没有数据文件//perror("LoadContact::fopen");return;}// 读文件PeoInfo tmp = { 0 }; // 存储读取到的数据// 每次读一个数据while (fread(&tmp, sizeof(PeoInfo), 1, pf)){// 检查容量,不够的话要扩容CheckCapacity(pc);// 存储从文件读取到的数据pc->data[pc->sz] = tmp;pc->sz++;}// 关闭文件fclose(pf);pf = NULL;
}

检查容量

再定义一个函数CheckCapacity,用于检查并扩容动态数组。函数的参数是一个指向Contact结构体的指针。函数的具体实现如下:

  1. 函数首先使用assert宏检查传入的指针是否有效。然后,它检查当前数组是否需要扩容。如果数组的大小已经等于容量,就需要扩容。
  2. 在需要扩容的情况下,函数使用realloc函数重新分配内存。realloc函数会尝试将原来分配的内存块扩大到指定的大小。如果扩容成功,realloc函数会返回一个指向新内存块的指针,否则返回NULL。
  3. 如果realloc函数返回NULL,说明扩容失败,函数会输出错误信息并返回0。如果realloc函数返回非空指针,说明扩容成功,函数会更新数组的起始位置和容量,并输出扩容成功的信息。最后,函数返回1,表示扩容成功或者不需要扩容。
// 扩容失败,返回0
// 扩容成功,不需要扩容,返回1
static int CheckCapacity(Contact* pc)
{// 检查指针有效性assert(pc);// 检查是否需要扩容if (pc->sz == pc->capacity){// 需要扩容PeoInfo* ptr = (PeoInfo*)realloc(pc->data, (pc->capacity + INC_SZ) * sizeof(PeoInfo));// 检查是否扩容成功if (ptr == NULL){// 扩容失败printf("CheckCapacity:%s\n", strerror(errno));return 0;}else{// 扩容成功// 更新数组的起始位置pc->data = ptr;// 更新容量pc->capacity += INC_SZ;printf("增容成功,当前容量:%d\n", pc->capacity);}}return 1;
}

销毁通讯录

再定义一个函数DestroyContact,用来销毁通讯录。函数的参数是一个指向Contact结构体的指针。函数的具体实现如下:

  1. 先使用assert宏检查指针的有效性。
  2. 释放动态数组占用的内存空间。
  3. 把结构体的变量置空。
void DestroyConact(Contact* pc)
{// 检查指针有效性assert(pc);// 释放内存空间free(pc->data);pc->data = NULL;pc->capacity = 0;pc->sz = 0;printf("释放内存.....\n");
}

添加联系人

接着定义一个函数AddContact,参数仍然是一个指向Contact结构体的指针,实现向通讯录中添加联系人的功能。具体实现如下:

  1. 首先检查传入的指针是否有效,如果无效则使用assert宏触发断言,程序终止。
  2. 调用CheckCapacity函数检查通讯录是否需要扩容,如果需要则进行扩容操作。如果扩容失败,则输出提示信息并返回。
  3. 如果扩容成功,则提示用户输入联系人信息,包括名字、年龄、性别、电话和地址。这些信息将被存储在通讯录的data数组中,下标为 sz。
  4. 最后更新有效数据个数sz,并输出添加成功的提示信息。
void AddContact(Contact* pc)
{// 检查指针有效性assert(pc);// 扩容,同时检查是否成功if (0 == CheckCapacity(pc)){// 扩容失败printf("空间不够,扩容失败\n");return;}else{// 输入联系人信息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].tele);printf("请输入地址:>");scanf("%s", pc->data[pc->sz].addr);// 更新有效数据个数pc->sz++;printf("添加成功\n");}
}

打印数据

接下来定义一个函数 ShowContact,用于打印联系人信息。函数接受一个指向Contact结构体的指针pc。具体实现如下:

  1. 函数首先使用assert宏检查指针pc是否有效,如果无效则程序会崩溃并输出错误信息。
  2. 接下来,函数使用printf函数打印联系人信息。首先打印表头,包括姓名、年龄、性别、电话和地址。然后使用循环遍历pc中的每个联系人,打印其姓名、年龄、性别、电话和地址。
  3. 在打印时使用格式化字符串,其中 %s 表示字符串,%-10s 表示左对齐并占用 10 个字符的字符串,%-4d 表示左对齐并占用 4 个字符的整数,%-5s 表示左对齐并占用 5 个字符的字符串,%-12s 表示左对齐并占用 12 个字符的字符串,%-30s 表示左对齐并占用 30 个字符的字符串。
void ShowContact(const Contact* pc)
{// 检查指针有效性assert(pc);// 打印效果// 姓名      年龄      性别     电话      地址// zhangsan 20        男      123456    北京//// 打印标题printf("%-10s %-4s %-5s %-12s %-30s\n", "姓名", "年龄", "性别", "电话", "地址");// 打印数据for (int i = 0; i < pc->sz; ++i){printf("%-10s %-4d %-5s %-12s %-30s\n",pc->data[i].name, pc->data[i].age, pc->data[i].sex, pc->data[i].tele, pc->data[i].addr);}
}

删除联系人

接下来实现通讯录的删除联系人功能。具体实现如下:

FindByName函数:根据指定名字,在通讯录中查找联系人信息。函数参数为指向Contact结构体的指针和要查找的名字。函数返回值为查找到的联系人在通讯录中的下标,如果没找到则返回-1。

DelContact函数:删除通讯录中指定联系人。函数参数为指向Contact结构体的指针。函数实现如下:

  1. 检查指针有效性,如果为空则直接返回。
  2. 检查通讯录中是否还有数据,如果没有则输出提示信息并返回。
  3. 获取用户输入的要删除的联系人名字。
  4. 调用FindByName函数查找要删除的联系人在通讯录中的下标。
  5. 如果没找到,则输出提示信息并返回。
  6. 如果找到了,则使用memmove函数将该联系人后面的所有联系人向前移动一个位置,覆盖掉要删除的联系人。
  7. 更新通讯录中的有效数据个数。
  8. 输出删除成功的提示信息。
// 根据指定名字,查找联系人信息
static int FindByName(const Contact* pc, char name[])
{// 检查指针有效性assert(pc);assert(name);// 遍历数组for (int i = 0; i < pc->sz; ++i){// 检查名字是否匹配if (0 == strcmp(pc->data[i].name, name)){// 找到了return i;}}// 没找到return -1;
}void DelContact(Contact* pc)
{// 检查指针有效性assert(pc);// 检查是否还有数据if (pc->sz == 0){// 通讯录已空printf("通讯录为空,无法删除\n");return;}char name[MAX_NAME] = { 0 }; // 存储用户输入的数据// 用户输入信息printf("输入要删除人的名字:>");scanf("%s", name);// 找到要删除的人的下标int pos = FindByName(pc, name);// 检查是否找到if (pos == -1){// 没找到printf("要删除的人不存在\n");return;}// 删除pos位置上的数据// 挪动pos后面的数据,覆盖posmemmove(pc->data + pos, pc->data + pos + 1, sizeof(PeoInfo) * (pc->sz - pos - 1));// 更新有效数据个数pc->sz--;printf("删除成功\n");
}

查找联系人

接着实现在通讯录中根据姓名查找联系人的功能。具体实现如下:

  1. 函数名为SearchContact,接受一个指向Contact结构体的指针pc作为参数。
  2. 第一行代码使用assert宏检查指针pc是否有效,如果pc为 NULL,则程序会崩溃并输出错误信息。
  3. 定义一个char类型的数组name,长度为MAX_NAME,用于存储用户输入的要查找的人的名字。
  4. 使用printf函数输出提示信息,让用户输入要查找的人的名字。
  5. 使用scanf函数读取用户输入的名字,存储到name数组中。
  6. 调用FindByName函数,在通讯录中查找名字为name的联系人,返回值为该联系人在通讯录中的位置,如果没找到则返回-1。
  7. 判断FindByName函数的返回值,如果为-1,则说明没有找到要查找的联系人,使用printf函数输出提示信息,并直接返回。
  8. 如果FindByName函数的返回值不为-1,则说明找到了要查找的联系人,使用printf函数输出通讯录的标题行,包括姓名、年龄、性别、电话、地址等信息。
  9. 使用printf函数输出找到的联系人的具体信息,包括姓名、年龄、性别、电话、地址等信息,这些信息都存储在Contact结构体中的data数组中,通过pc指针访问。pos变量表示要查找的联系人在data数组中的位置。注意,这里使用了%-10s、%-4d等格式控制符,表示输出字符串时左对齐,并且占用固定的宽度,方便对齐。
void SearchContact(const Contact* pc)
{// 检查指针有效性assert(pc);char name[MAX_NAME] = { 0 }; // 存储用户输入的信息// 用户输入数据printf("请输入要查找人的名字:>");scanf("%s", name);// 查找int pos = FindByName(pc, name);// 检查是否找到if (pos == -1){// 没找到printf("要查找的人不存在\n");return;}// 打印标题行printf("%-10s %-4s %-5s %-12s %-30s\n", "姓名", "年龄", "性别", "电话", "地址");// 打印数据printf("%-10s %-4d %-5s %-12s %-30s\n",pc->data[pos].name,pc->data[pos].age,pc->data[pos].sex,pc->data[pos].tele,pc->data[pos].addr);
}

修改联系人

再下来实现一个函数,函数的参数仍然是一个指向通讯录结构体类型Contact的指针,用于修改通讯录中的联系人信息。具体实现如下:

  1. 使用assert宏函数检查指针有效性,如果指针为空,则程序会终止。
  2. 定义一个char类型数组name,用于存储用户输入的联系人名字。
  3. 使用printf函数提示用户输入要修改的联系人名字,并使用scanf函数读取用户输入的名字。
  4. 调用FindByName函数查找通讯录中是否存在该联系人,如果不存在则输出提示信息并返回。
  5. 如果存在该联系人,则使用scanf函数分别读取用户输入的联系人信息,包括名字、年龄、性别、电话和地址,并将这些信息存储到通讯录结构体中对应的位置。
  6. 最后使用printf函数输出修改成功的提示信息。
void ModifyContact(Contact* pc)
{// 检查指针有效性assert(pc);char name[MAX_NAME] = { 0 }; // 存储用户输入的信息// 用户输入数据printf("请输入要修改人的名字:>");scanf("%s", name);// 查找int pos = FindByName(pc, name);// 检查是否找到if (pos == -1){// 没找到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].tele);printf("请输入地址:>");scanf("%s", pc->data[pos].addr);printf("修改成功\n");
}

排序通讯录

接下来实现排序功能。具体实现如下:

  1. 定义一个比较函数CmpByName,用于按照名字来排序联系人信息。该函数的参数为两个指向联系人信息结构体的指针p1和p2,返回值为两个名字字符串的比较结果。
  2. 在CmpByName函数中,首先使用assert宏检查指针p1和p2的有效性,确保程序不会因为无效指针而崩溃。然后使用strcmp函数比较两个联系人信息结构体中的名字字符串大小,返回比较结果。
  3. 定义一个排序函数SortContact,用于对联系人信息进行排序。该函数的参数为一个指向联系人管理系统结构体的指针pc。
  4. 在SortContact函数中,首先使用assert宏检查指针pc的有效性,确保程序不会因为无效指针而崩溃。然后调用qsort函数对联系人信息进行排序,其中pc->data表示联系人信息数组的首地址,pc->sz表示联系人信息数组的大小,sizeof(PeoInfo)表示每个联系人信息结构体的大小,CmpByName表示排序函数。
  5. 最后输出排序成功的提示信息。
// 按照名字来排序
int CmpByName(const void* p1, const void* p2)
{// 检查指针有效性assert(p1 && p2);// 比较名字字符串大小return strcmp(((PeoInfo*)p1)->name, ((PeoInfo*)p2)->name);
}void SortContact(Contact* pc)
{// 检查指针有效性assert(pc);// 根据名字来排序qsort(pc->data, pc->sz, sizeof(PeoInfo), CmpByName);printf("排序成功\n");
}

保存通讯录

最后实现将联系人信息保存到文件中的功能。具体实现如下:

  1. 函数定义:函数名为SaveContact,参数为一个指向Contact结构体的指针pc。
  2. 检查指针有效性:使用assert宏函数检查指针pc是否为NULL,如果是NULL则程序会直接终止。
  3. 打开文件:使用fopen函数打开名为"contact.dat"的文件,以二进制写入模式(“wb”)打开。返回值为一个指向FILE结构体的指针pf。
  4. 检查是否打开成功:使用if语句判断指针pf是否为NULL,如果是NULL则说明打开文件失败,使用perror函数输出错误信息并返回。
  5. 写数据:使用for循环遍历pc指向的Contact结构体中的所有联系人信息,使用fwrite函数将每个联系人信息写入文件中。其中,第一个参数为指向联系人信息的指针,第二个参数为每个联系人信息的大小,第三个参数为写入的数量,第四个参数为指向文件的指针pf。
  6. 关闭文件:使用fclose函数关闭文件,释放文件指针pf所占用的资源。将pf赋值为NULL,防止出现野指针。
  7. 输出保存成功信息:使用printf函数输出保存成功的信息。
void SaveContact(Contact* pc)
{// 检查指针有效性assert(pc);// 打开文件FILE* pf = fopen("contact.dat", "wb");// 检查是否打开成功if (pf == NULL){// 打开文件失败perror("SaveContact::fopen");return;}// 写数据,一次写一个for (int i = 0; i < pc->sz; ++i){fwrite(pc->data + i, sizeof(struct PeoInfo), 1, pf);}//关闭文件fclose(pf);pf = NULL;printf("保存成功...\n");
}

总结

  1. 动态内存管理一定要熟练掌握,包括使用malloc开辟空间,使用realloc扩容,使用free函数释放空间等。
  2. 常见的文件操作函数得会用,包括fopen,fclose以及文件的顺序读写和随机读写操作等。
  3. 顺序表的增删查改、排序打印等常规操作一定要掌握,通讯录的本质就是顺序表。

感谢大家的阅读!

相关文章:

C语言动态内存管理与文件操作:打造高效通讯录

本篇博客会讲解如何使用C语言实现一个通讯录。实现通讯录的过程中&#xff0c;会大量用到C语言的知识点&#xff0c;包括但不限于&#xff1a;函数、自定义类型、指针、动态内存管理、文件操作&#xff0c;这些知识点在我的其他博客中都有讲解过&#xff0c;欢迎大家阅读&#…...

2001-2021年全国30省就业人数数据

2001-2021年全国30省就业人数数据/各省就业人数数据 1、时间&#xff1a;2001-2021年 2、范围&#xff1a;包括30个省市不含西藏 3、指标&#xff1a;就业人数 4、来源&#xff1a;各省NJ、社会统计NJ 5、缺失情况说明&#xff1a;无缺失 6、指标说明&#xff1a; 就业人…...

自然语言处理知识抽取(pkuseg、DDParser安装及使用)

一、分词简介 1.基本概念 分词是自然语言处理中的一个重要步骤&#xff0c;它可以帮助我们将文本分成一个个词语&#xff0c;以便更好地理解和分析文本。在计算机视觉、语音识别、机器翻译等领域&#xff0c;分词都扮演着重要的角色。 目前&#xff0c;常用的分词库包括 jie…...

Linux内核面试知识总结

Linux启动过程 1、主机加电自检&#xff0c;加载BIOS硬件信息 2、读取MBR引导文件 3、引导linux内核 4、启动第一个进程init&#xff08;进程号永远为1&#xff09; 5、进度相应的运行级别 6、运行终端&#xff0c;输入用户名和密码 linux系统缺省的运行级别 关机、单机…...

深度学习模型压缩与优化加速

1. 简介 深度学习&#xff08;Deep Learning&#xff09;因其计算复杂度或参数冗余&#xff0c;在一些场景和设备上限制了相应的模型部署&#xff0c;需要借助模型压缩、系统优化加速、异构计算等方法突破瓶颈&#xff0c;即分别在算法模型、计算图或算子优化以及硬件加速等层…...

Kali 更换源(超详细,附国内优质镜像源地址)

1.进入管理员下的控制台。 2. 输入密码后点击“授权”。 3.在控制台内输入下面的内容。 vim /etc/apt/sources.list 4.敲击回车后会进入下面的页面。 5.来到这个页面后的第一部是按键盘上的“i”键&#xff0c;左下角出现“插入”后说明操作正确。 6.使用“#”将原本的源给注释…...

Java版工程项目管理系统平台+java版企业工程系统源码+助力工程企业实现数字化管理

Java版工程项目管理系统 Spring CloudSpring BootMybatisVueElementUI前后端分离 功能清单如下&#xff1a; 首页 工作台&#xff1a;待办工作、消息通知、预警信息&#xff0c;点击可进入相应的列表 项目进度图表&#xff1a;选择&#xff08;总体或单个&#xff09;项目显示1…...

搜索引擎测试报告

文章目录 一、项目背景二、项目功能三、测试目的四、测试环境五、测试计划1、功能测试2、自动化测试 六、测试结果 一、项目背景 java官方文档是我们在学习java语言中不可或缺的权威资料。相比于各种网站的Java资料&#xff0c;官方文档无论是语言表达还是组织方式都要更加全面…...

4年的测试工程师,你遇到过自身瓶颈期吗?又是怎样度过的?

从毕业到现在已经快4年啦&#xff0c;一直软件测试行业混迹。我不是牛人&#xff0c;但是自我感觉还算是个合格的测试工程师&#xff0c;有必要写下自己将近4年来的经历&#xff0c;给自我以提示&#xff0c;给刚入行的朋友提供点参考。 貌似这一点适应的行业最广&#xff0c;…...

【Python零基础学习入门篇④】——第四节:Python的列表、元组、集合和字典

⬇️⬇️⬇️⬇️⬇️⬇️ ⭐⭐⭐Hello&#xff0c;大家好呀我是陈童学哦&#xff0c;一个普通大一在校生&#xff0c;请大家多多关照呀嘿嘿&#x1f601;&#x1f60a;&#x1f618; &#x1f31f;&#x1f31f;&#x1f31f;技术这条路固然很艰辛&#xff0c;但既已选择&…...

3.6 cache存储器

学习步骤&#xff1a; 我会采取以下几个步骤来学习Cache存储器&#xff1a; 确定学习目标&#xff1a;Cache存储器作为一种高速缓存存储器&#xff0c;通常用于提高计算机系统的运行效率。因此&#xff0c;我需要明确学习Cache存储器的目的&#xff0c;包括了解其原理、结构和…...

Ubuntu零基础安装

Ubuntu零基础安装 首先我们需要安装VM&#xff0c;再安装ubuntu。 1、安装VM 进入VM官网 VM官网地址 选择下载试用版 下载Windows版本 下载完成后&#xff0c;点击安装包进行安装 至此就安装完毕了。 桌面会出现VM的图标。 点击打开&#xff0c;弹出如下画面&#xff1a; …...

热门的常用 API 大全分享

天气/环境 空气质量查询&#xff1a; 查询国内3400个城市的整点观测&#xff0c;获取指定城市的整点观测空气质量。未来7天生活指数&#xff1a;支持国内3400个城市以及国际4万个城市的天气指数数据&#xff0c;包括晨练、洗车、穿衣&#xff08;12项&#xff0c;有详细说明&a…...

利用粒子群算法设计无线传感器网络中的最优安全路由模型(Matlab代码实现)

目录 &#x1f4a5;1 概述 &#x1f4da;2 运行结果 &#x1f389;3 参考文献 &#x1f468;‍&#x1f4bb;4 Matlab代码 &#x1f4a5;1 概述 无线传感器网络&#xff08;WSN&#xff09;由数十个、数百个甚至数千个自主传感器组成。这些传感器以无线方式嵌入环境中&…...

2023年华东杯数学建模B 题 期货价格相关性问题-思路解析

题目背景&#xff1a; 许多金融标的都有其内在的关联&#xff0c;如何从量价数据找到这种关联是一个有趣的 问题。例如在万得的“煤焦钢矿”板块中&#xff0c;有螺纹钢、铁矿石、不锈钢、热轧卷板、 硅铁、焦煤、焦炭、锰硅、线材 9 个品种。这些品种有些是上下游关系&…...

SAP UI5 之Controls (控件) 笔记三

文章目录 官网 Walkthrough学习-Controls控件1.0.1 在index.html中使用class id 属性控制页面展示的属性1.0.2 我们在index.js文件中引入 text文本控制1.0.3打开浏览器查看结果 官网 Walkthrough学习-Controls控件 Controls控件 在前面展示在浏览器中的Hello World 是在Html …...

哈希表题目:设计地铁系统

文章目录 题目标题和出处难度题目描述要求示例数据范围 解法思路和算法代码复杂度分析 题目 标题和出处 标题&#xff1a;设计地铁系统 出处&#xff1a;1396. 设计地铁系统 难度 6 级 题目描述 要求 一个地铁系统正在收集乘客在不同站之间的花费时间。他们在使用这些数…...

云时通OMS:为零售品牌商打造高效的全渠道订单管理!

传统的零售企业围绕“人、货、场” 三要素来展开营销&#xff0c;其目标是基于“场”将货销售给更多的人。随着数字技术的应用&#xff0c;新零售模式下的“场”除了传统的线下店铺外&#xff0c;还拓展了多元化的线上渠道&#xff0c;比如小程序、企业APP、第三方平台、电商直…...

有必要给孩子买台灯吗?分享四款高品质的护眼台灯

有必要使用护眼台灯&#xff0c;尤其是有近视现象的孩子们。 现在很多孩子小学就开始近视了&#xff0c;保护视力刻不容缓呀! 很多人不知道&#xff0c;其实劣质光线是最大的眼睛杀手 给孩子随便买便宜的台灯&#xff0c;看着一样能用&#xff0c;其实时间久了 对孩子眼睛的…...

模板方法模式

模板方法模式 模板方法模式定义:使用场景角色定义抽象模板: 为抽象模板&#xff0c;它的方法分为两类AbstractClass1. 基本方法: 也叫做基本操作&#xff0c;是由子类实现的方法&#xff0c;并且在模板方法被调用。2. 模板方法: 可以有一个或几个&#xff0c;一般是一个具体方法…...

[2025CVPR]DeepVideo-R1:基于难度感知回归GRPO的视频强化微调框架详解

突破视频大语言模型推理瓶颈,在多个视频基准上实现SOTA性能 一、核心问题与创新亮点 1.1 GRPO在视频任务中的两大挑战 ​安全措施依赖问题​ GRPO使用min和clip函数限制策略更新幅度,导致: 梯度抑制:当新旧策略差异过大时梯度消失收敛困难:策略无法充分优化# 传统GRPO的梯…...

渲染学进阶内容——模型

最近在写模组的时候发现渲染器里面离不开模型的定义,在渲染的第二篇文章中简单的讲解了一下关于模型部分的内容,其实不管是方块还是方块实体,都离不开模型的内容 🧱 一、CubeListBuilder 功能解析 CubeListBuilder 是 Minecraft Java 版模型系统的核心构建器,用于动态创…...

云原生玩法三问:构建自定义开发环境

云原生玩法三问&#xff1a;构建自定义开发环境 引言 临时运维一个古董项目&#xff0c;无文档&#xff0c;无环境&#xff0c;无交接人&#xff0c;俗称三无。 运行设备的环境老&#xff0c;本地环境版本高&#xff0c;ssh不过去。正好最近对 腾讯出品的云原生 cnb 感兴趣&…...

【无标题】路径问题的革命性重构:基于二维拓扑收缩色动力学模型的零点隧穿理论

路径问题的革命性重构&#xff1a;基于二维拓扑收缩色动力学模型的零点隧穿理论 一、传统路径模型的根本缺陷 在经典正方形路径问题中&#xff08;图1&#xff09;&#xff1a; mermaid graph LR A((A)) --- B((B)) B --- C((C)) C --- D((D)) D --- A A -.- C[无直接路径] B -…...

jmeter聚合报告中参数详解

sample、average、min、max、90%line、95%line,99%line、Error错误率、吞吐量Thoughput、KB/sec每秒传输的数据量 sample&#xff08;样本数&#xff09; 表示测试中发送的请求数量&#xff0c;即测试执行了多少次请求。 单位&#xff0c;以个或者次数表示。 示例&#xff1a;…...

永磁同步电机无速度算法--基于卡尔曼滤波器的滑模观测器

一、原理介绍 传统滑模观测器采用如下结构&#xff1a; 传统SMO中LPF会带来相位延迟和幅值衰减&#xff0c;并且需要额外的相位补偿。 采用扩展卡尔曼滤波器代替常用低通滤波器(LPF)&#xff0c;可以去除高次谐波&#xff0c;并且不用相位补偿就可以获得一个误差较小的转子位…...

MySQL的pymysql操作

本章是MySQL的最后一章&#xff0c;MySQL到此完结&#xff0c;下一站Hadoop&#xff01;&#xff01;&#xff01; 这章很简单&#xff0c;完整代码在最后&#xff0c;详细讲解之前python课程里面也有&#xff0c;感兴趣的可以往前找一下 一、查询操作 我们需要打开pycharm …...

【Linux】Linux安装并配置RabbitMQ

目录 1. 安装 Erlang 2. 安装 RabbitMQ 2.1.添加 RabbitMQ 仓库 2.2.安装 RabbitMQ 3.配置 3.1.启动和管理服务 4. 访问管理界面 5.安装问题 6.修改密码 7.修改端口 7.1.找到文件 7.2.修改文件 1. 安装 Erlang 由于 RabbitMQ 是用 Erlang 编写的&#xff0c;需要先安…...

【Kafka】Kafka从入门到实战:构建高吞吐量分布式消息系统

Kafka从入门到实战:构建高吞吐量分布式消息系统 一、Kafka概述 Apache Kafka是一个分布式流处理平台,最初由LinkedIn开发,后成为Apache顶级项目。它被设计用于高吞吐量、低延迟的消息处理,能够处理来自多个生产者的海量数据,并将这些数据实时传递给消费者。 Kafka核心特…...

2025年低延迟业务DDoS防护全攻略:高可用架构与实战方案

一、延迟敏感行业面临的DDoS攻击新挑战 2025年&#xff0c;金融交易、实时竞技游戏、工业物联网等低延迟业务成为DDoS攻击的首要目标。攻击呈现三大特征&#xff1a; AI驱动的自适应攻击&#xff1a;攻击流量模拟真实用户行为&#xff0c;差异率低至0.5%&#xff0c;传统规则引…...