当前位置: 首页 > 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;一般是一个具体方法…...

基于Yolov5的NEU-DET钢材表面缺陷检测,优化组合新颖程度较高:CVPR2023 DCNV3和InceptionNeXt,涨点明显

1.钢铁缺陷数据集介绍 NEU-DET钢材表面缺陷共有六大类,分别为:crazing,inclusion,patches,pitted_surface,rolled-in_scale,scratches 每个类别分布为: 训练结果如下: 2.基于yolov5s的训练 map值: 2.1 Inception-MetaNeXtStage 对应博客:https://cv2023.blog.csdn.n…...

【HarmonyOS】自定义组件之ArkUI实现通用标题栏组件

【关键字】 标题栏、常用内置组件整合、ArkUI、自定义组件 1、写在前面 在上一篇文章中我们通过Java语言实现了一个通用的标题栏组件&#xff0c;有需要的可以看下&#xff0c;文章地址&#xff1a; 华为开发者论坛 现在很多朋友都已经转战ArkTS语言了&#xff0c;那么今天…...

C#开发的OpenRA游戏的加载地图流程

C#开发的OpenRA游戏的加载地图流程 OpenRA游戏里,地图是一个很关键的数据, 因为地图里包括了地面状态,地面上建筑物状态, 还有玩家在地图上的布局情况,以及各种活动限制的条件。 在OpenRA里,需要把地图目录:OpenRA\mods\cnc\maps 里所有的文件进行加载, 并且保存在缓…...

python ast 详解与用法

目录 基本概念节点类型ast.Assignast.Nameast.Constantast.Callast.Attribute 结点的遍历ast源码示例 结点的修改示例 参考链接 基本概念 在 python 中&#xff0c;我们可以通过自带的 ast 模块来对解析遍历语法树&#xff0c;通过ast.parse()可以将字符串代码解析为抽象语法树…...

Go语言开发小技巧易错点100例(七)

往期回顾&#xff1a; Go语言开发小技巧&易错点100例&#xff08;一&#xff09;Go语言开发小技巧&易错点100例&#xff08;二&#xff09;Go语言开发小技巧&易错点100例&#xff08;三&#xff09;Go语言开发小技巧&易错点100例&#xff08;四&#xff09;Go…...

爬虫为什么需要ip

爬虫需要使用爬虫ip主要是为了解决以下问题&#xff1a; 1、反爬虫机制&#xff1a;许多网站会设置反爬虫机制来防止爬虫程序的访问&#xff0c;例如限制IP地址的访问频率、检测访问来源等。使用爬虫ip可以绕过这些限制&#xff0c;使得爬虫程序更难被检测到。 2、访问限制&a…...

RabbitMQ-保证消息可靠性

RabbitMQ-保证消息可靠性 1.消息可靠性1.1.生产者消息确认1.1.1.修改配置1.1.2.定义Return回调1.1.3.定义ConfirmCallback 1.2.消息持久化1.2.1.交换机持久化1.2.2.队列持久化1.2.3.消息持久化 1.3.消费者消息确认1.3.1.演示none模式1.3.2.演示auto模式 1.4.消费失败重试机制1.…...

Python教程——Python本地环境安装

文章目录 简介安装Python下载安装验证安装结果 手动添加环境变量安装问题 简介 python官网&#xff1a;https://www.python.org/ Python Windows下载地址&#xff1a;https://www.python.org/downloads/windows/ Python 官方文档&#xff1a;https://www.python.org/doc/ Pytho…...

“智慧交通”转型升级+创新发展策略

随着“互联网交通”的应用创新推陈出新&#xff0c;传统轨道交通行业客户服务中心已难以满足乘客对便捷高效的客户服务需求&#xff1b;节假日人流量激增&#xff0c;客户服务人手不足&#xff0c;交通、站点堵塞、信息更新不及时等问题是常态。因此&#xff0c;“智慧城市”交…...

华为OD机试 - 开放日活动、取出尽量少的球(Python)

题目描述 某部门开展Family Day开放日活动,其中有个从桶里取球的游戏,游戏规则如下: 有N个容量一样的小桶等距排开, 且每个小桶都默认装了数量不等的小球, 每个小桶装的小球数量记录在数组 bucketBallNums 中, 游戏开始时,要求所有桶的小球总数不能超过SUM, 如果…...

网站上怎么做产品介绍/排名函数

1. 涉及平台 平台管理、商家端&#xff08;PC端、手机端&#xff09;、买家平台&#xff08;H5/公众号、小程序、APP端&#xff08;IOS/Android&#xff09;、微服务平台&#xff08;业务服务&#xff09; 2. 核心架构 Spring Cloud、Spring Boot、Mybatis、Redis 3. 前端框架…...

最新新闻热点及观点/电子商务沙盘seo关键词

set&#xff08;集合&#xff09; 直接创建一个空集合set_empty set() print(set_empty) # set() 根据参数创建# 根据参数 set_argument set(42,345,ry) print(set_argument) # 这样会报错,因为set只允许有一个参数 根据列表来创建set_list set([11,11,45,11,ee]) print(set…...

新疆建设兵团一师网站/万网域名

本文章是❤️力扣 (LeetCode)❤️的内容&#xff0c;该专栏还有多篇优质内容在等待你观看&#xff0c;现在点击右上角点击这个————&#x1f680;订阅专栏&#x1f680; &#x1f506;坚持刷算法 &#x1f48e;每天进步一点点 &#x1f680;冲冲冲冲冲冲冲冲冲冲 &#x1f4…...

齐河网站建设/小说排行榜百度

高中信息技术教学中存在的问题及对策分析时间&#xff1a;2014-08-04栏目&#xff1a;高中信息技术教学中存在的问题及对策分析高中信息技术教学中存在的问题及对策分析文/谭方芳摘要&#xff1a;我国在教育信息化和基础教育方面不断发生重大改革&#xff0c;一系列的政策改革措…...

中国移动积分兑换商城官方网站/成都网站快速开发

c 后台开发岗技能知识树 本质都是实力的提高&#xff0c;包含软实力和硬实力 学的深不深&#xff0c;跟你的基础有关 核心的点一定要在 硬技能&#xff1a; 1.语言 对象的声明周期&#xff1a;垃圾回收&#xff0c;对象声明周期&#xff0c;标准库&#xff0c;异常处理&a…...

网站找谁做/美国新冠疫情最新消息

Maven 进阶Maven 依赖机制传统方式Maven 的方式解释说明Maven POMPOM 的例子Maven 插件插件类型Maven 快照什么是快照&#xff1f;快照与版本Maven 依赖机制 在 Maven 依赖机制的帮助下自动下载所有必需的依赖库&#xff0c;并保持版本升级。让我们看一个案例研究&#xff0c;…...