精通C语言:打造高效便捷的通讯录管理系统

pFp8UCq.jpg

✨✨ 欢迎大家来到贝蒂大讲堂✨✨

🎈🎈养成好习惯,先赞后看哦~🎈🎈

所属专栏:C语言项目
贝蒂的主页:Betty‘s blog

引言

在我们大致学习完C语言之后,我们就可以利用目前所学的知识去做一些有意思的项目,而今天贝蒂就带大家完成一个通讯录的简易实现,

本章你可能需要的知识:

  1. 动态内存的使用:动态内存管理
  2. 文件的创建与使用:文件操作

1. 通讯录要求

  1. 通讯录包括每个人的姓名,性别,年龄,电话与地址。
  2. 玩家可以自由选择通讯录的进出。
  3. 玩家可以自由增删查改通讯录中的数据。

2. 多文件管理

为了方便代码的管理和保证通讯录实现逻辑的清晰性,我们将采用多文件管理的模式。

  1. 创建头文件contact.c,包含所有头文件(其他源文件只需引用它即可),以及所有通讯录功能的展现。

  2. 创建源文件contact.c,负责通讯录所有功能的具体代码实现。

  3. 创建源文件text.c,负责展现通讯录实现的总体逻辑。

3. 通讯录的准备

3.1 预处理信息

为了方便我们后续更换通讯的信息,我们可以利用来定义通讯录的具体信息的大小。

#define MAX 100//最大人数
#define MAX_NAME 20//名字最大长度
#define MAX_SEX 5//性别最大长度
#define MAX_TELE 12//电话最大长度
#define MAX_ADDR 30//地址最大长度

3.2 结构体定义

每个人的通讯录都要包含姓名,性别,年龄,电话与地址等信息,这时就需要我们创建一个结构体来方便管理。

typedef struct PeoInfo
{char name[MAX_NAME];//名字int age;//年龄char sex[MAX_SEX];//性别char tele[MAX_TELE];//电话char addr[MAX_ADDR];//地址
}PeoInfo;

而我们需要用这个结构体创建一个大小为100的数组,并且我们还需要知道当前通讯录的大小才能进行增删查改的操作,这两者息息相关,为了简化代码和增加代码的可读性,我们可以将这两者重新定义一个结构体。

typedef struct contact
{PeoInfo data[MAX];//一百个人的数据int sz;//通讯录的大小
}contact;

4. 简易菜单

void menu()
{printf("***********************************\n");printf("**    1.add          2.delete    **\n");printf("**    3.search       4.modify    **\n");printf("**    5.display      6.sort      **\n");printf("**             0.exit            **\n");printf("***********************************\n");
}

画面展示:

5. 通讯录具体功能

5.1 初始化

我们首先对通讯录进行初始化。

void InitContact(contact* pc)//初始化
{assert(pc);pc->sz = 0;memset(pc->data, 0, sizeof(pc->data));
}

5.2 展示联系人

当用户选择5时自动展示通讯录中的用户,并且展示用户过程中需要进行对齐,便于用户观看。

void DisplayContact(contact* pc)//打印信息
{assert(pc);printf("%-15s\t%-5s\t%-5s\t%-12s\t%-30s\n", "姓名", "年纪","性别", "电话", "地址");//默认右对齐,修改为左对齐//中间也要留下足够的空间for (int i = 0; i < pc->sz; i++){printf("%-15s\t%-5d\t%-5s\t%-12s\t%-30s\n", pc->data[i].name,pc->data[i].age,pc->data[i].sex,pc->data[i].tele,pc->data[i].addr);}
}

5.3 添加联系人

用户选择1可以自由添加联系人,如果通讯录已满,则提醒用户通讯录已满,请先清理通讯录。

(1) 检查通讯录是否已满

如果通讯录满了返回0,未满则返回1。

int CheckContact(contact*pc)//检查大小
{assert(pc);if (pc->sz == 100){return 0;}return 1;
}
(2) 添加

当通讯录未满时,用户可以输入数据添加新用户。

void AddContact(contact* pc)//增加联系人
{assert(pc);int ret = CheckContact(pc);//检查是否满了if (ret == 0){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].tele);printf("请输入联系人的地址:> ");scanf("%s", pc->data[pc->sz].addr);printf("用户添加成功!\n");pc->sz++;
}

5.4 删除联系人

用户可以选择2清理通讯录,删除指定联系人

(1) 寻找下标

在删除指定练习人时我们需通过其姓名寻找该联系人的下标。找到返回其下标,否则返回-1、

int FindName(contact* pc, char name[])
{assert(pc&&name);for (int pos = 0; pos < pc->sz; pos++){if (strcmp(pc->data[pos].name, name) == 0){return pos;}}return -1;
}
(2) 删除

通过寻找到的下标,我们可以利用后面的数据依次覆盖来达到删除的目的。

注意:我们不能覆盖最后一个数据否则就会发生数组越界,这时我们只需减去通讯录此时的大小就好了。

void DeleteContact(contact* pc)//删除联系人
{assert(pc);assert(pc->sz >= 0);char name[MAX_NAME];printf("请输入需要删除人的姓名:> ");scanf("%s", name);int pos = FindName(pc, name);if (pos == -1){printf("通讯录中并没有这个人!!\n");return;}for (int i = pos; i < pc->sz - 1; i++){pc->data[i] = pc->data[i + 1];//覆盖}printf("删除成功\n");pc->sz--;
}

删除前:

删除后 :

5.5 查找联系人

通过选择3查找指定联系人,没有找到则提醒用户没有该用户,查找到就打印其信息。

void SearchContact(contact* pc)//查找联系人
{assert(pc);char name[MAX_NAME];printf("请输入需要查找人的姓名:> ");scanf("%s", name);int pos = FindName(pc, name);if (pos == -1){printf("通讯录中并没有这个人!!\n");return;}printf("%-15s\t%-5s\t%-5s\t%-12s\t%-30s\n", "姓名", "年纪","性别", "电话", "地址");printf("%-15s\t%-5d\t%-5s\t%-12s\t%-30s", pc->data[pos].name,pc->data[pos].age,pc->data[pos].sex,pc->data[pos].tele,pc->data[pos].addr);
}

5.6 修改联系人

我们可以通过选择4修改指定联系人的信息。

void ModifyContact(contact* pc)//修改联系人
{assert(pc);char name[MAX_NAME];printf("请输入需要修改人的姓名:> ");scanf("%s", name);int pos = FindName(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");
}

修改前:

修改后:

5.7 排序联系人

我们可以选择6对通讯录进行排序,可以按照姓名,年纪,性别,电话,地址排序。

int cmp1(const void* p1, const void* p2)
{return strcmp(((PeoInfo*)p1)->name, ((PeoInfo*)p2)->name);
}
int cmp2(const void* p1, const void* p2)
{return ((PeoInfo*)p1)->age- ((PeoInfo*)p2)->age;
}
int cmp3(const void* p1, const void* p2)
{return strcmp(((PeoInfo*)p1)->sex, ((PeoInfo*)p2)->sex);
}
int cmp4(const void* p1, const void* p2)
{return strcmp(((PeoInfo*)p1)->tele, ((PeoInfo*)p2)->tele);
}
int cmp5(const void* p1, const void* p2)
{return strcmp(((PeoInfo*)p1)->addr, ((PeoInfo*)p2)->addr);
}
void SortByName(contact* pc)
{qsort(pc, pc->sz, sizeof(PeoInfo), cmp1);
}
void SortByAge(contact* pc)
{qsort(pc, pc->sz, sizeof(PeoInfo), cmp2);
}
void SortBySex(contact* pc)
{qsort(pc, pc->sz, sizeof(PeoInfo), cmp3);
}
void SortByTele(contact* pc)
{qsort(pc, pc->sz, sizeof(PeoInfo), cmp4);
}
void SortByAddr(contact* pc)
{qsort(pc, pc->sz, sizeof(PeoInfo), cmp4);
}
void SortContact(contact* pc)//排序联系人
{assert(pc);printf("请选择如何排序:> ");char input[20];scanf("%s", input);if (strcmp(input, "姓名")==0){SortByName(pc);//按姓名排序}else if (strcmp(input, "年龄") == 0){SortByAge(pc);//按年龄排序}else if (strcmp(input, "性别") == 0){SortBySex(pc);//按性别排序}else if (strcmp(input, "电话") == 0){SortByTele(pc);//按电话排序}else if (strcmp(input, "地址") == 0){SortByAddr(pc);//按地址排序}else{printf("输入非法,请重新输入\n");}
}

6. 改进通讯录

6.1 动态内存开辟

上述通讯录有一个致命的缺点——通讯录大小固定,为了解决这个问题我可以使用我们前面学过的动态内存开辟

(1) 新增变量

为了方便我们知道此时的容量大小,我们将在结构体中加入新的变量。

typedef struct contact
{PeoInfo *data;//动态开辟的数据int sz;//通讯录的大小int capacity;//通讯录的容量
}contact;
(2) 初始化
void InitContact(contact* pc)//初始化
{assert(pc);pc->sz = 0;pc->data = (PeoInfo*)calloc(2, sizeof(PeoInfo));if (pc->data == NULL){perror("InitContact:");return;}pc->capacity = 2;/*memset(pc->data, 0, sizeof(pc->data));*/
}
(3) 增容

当通讯录用户数量满时增加内存空间。

void CheckCapacity(contact* pc)//增容
{if (pc->sz == pc->capacity){PeoInfo* tmp = (PeoInfo*)realloc(pc->data, (pc->capacity + 2) * sizeof(PeoInfo));if (tmp != NULL){pc->data = tmp;}else{perror("CheckCapacity:");return;}pc->capacity += 2;printf("增容成功\n");}
}

6.2 文件保存

我们发现每当我们关闭程序时我们写入的数据会被清空,我们要想保存上次写入的数据就应该使用文件操作

(1) 加载数据

每次初始化时就要将上次文件的数据导入进来。

void LoadContact(contact* pc)//加载上次数据
{PeoInfo tmp = { 0 };FILE* pf = fopen("contact.txt", "rb");if (pf == NULL){return;}//读取文件,存放到通讯录while (fread(&tmp, sizeof(PeoInfo), 1, pf))//返回值为0就是遇见文件末尾{CheckCapacity(pc);pc->data[pc->sz] = tmp;pc->sz++;}fclose(pf);pf = NULL;
}
(2) 保存数据

当程序正常结束时保存数据。

void SaveContact(contact* pc)//保存数据
{FILE* pfWrite = fopen("contact.txt", "wb");if (pfWrite == NULL){perror("fopen:");return;}//写通讯录中数据到文件中int i = 0;for (i = 0; i < pc->sz; i++){fwrite(&(pc->data[i]), sizeof(PeoInfo), 1, pfWrite);}fclose(pfWrite);pfWrite = NULL;printf("保存成功\n");}

7. 通讯录逻辑的搭建

  1. 用户通过数字选择功能。
  2. 每次选择完之后返回菜单。
  3. 选择退出之后保存数据,程序结束。
enum Option//利用枚举增加代码的可读性
{EXIT,//0ADD,DEL,SEARCH,MODIFY,DISPLAY,SORT
};
void test()
{int input = 0;contact con;InitContact(&con);do{menu();printf("请输入你的选择:>");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 DISPLAY:DisplayContact(&con);break;case SORT:SortContact(&con);break;case EXIT:SaveContact(&con);//退出保存数据DestroyContact(&con);break;default:printf("非法输入,请重新输入\n");break;}} while (input);
}
int main()
{test();return 0;
}

8. 完整代码

8.1 contact.h

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<assert.h>
#define MAX_NAME 20//名字最大长度
#define MAX_SEX 5//性别最大长度
#define MAX_TELE 12//电话最大长度
#define MAX_ADDR 30//地址最大长度
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;//动态开辟的数据int sz;//通讯录的大小int capacity;//通讯录的容量
}contact;
void InitContact(contact* pc);//初始化
void DisplayContact(contact* pc);//打印信息
void AddContact(contact* pc);//增加联系人
void DeleteContact(contact* pc);//删除联系人
void SearchContact(contact* pc);//查找联系人
void ModifyContact(contact* pc);//修改联系人
void SortContact(contact* pc);//排序联系人
void SaveContact(contact* pc);//保存数据
void DestroyContact(contact* pc);//销毁内存

8.2 contact.c

#include"contact.h"
void CheckCapacity(contact* pc)//增容
{if (pc->sz == pc->capacity){PeoInfo* tmp = (PeoInfo*)realloc(pc->data, (pc->capacity + 2) * sizeof(PeoInfo));if (tmp != NULL){pc->data = tmp;}else{perror("CheckCapacity:");return;}pc->capacity += 2;printf("增容成功\n");}
}
void LoadContact(contact* pc)//加载上次数据
{PeoInfo tmp = { 0 };FILE* pf = fopen("contact.txt", "rb");if (pf == NULL){return;}//读取文件,存放到通讯录while (fread(&tmp, sizeof(PeoInfo), 1, pf))//返回值为0就是遇见文件末尾{CheckCapacity(pc);pc->data[pc->sz] = tmp;pc->sz++;}fclose(pf);pf = NULL;
}void InitContact(contact* pc)//初始化
{assert(pc);pc->sz = 0;pc->data = (PeoInfo*)calloc(2, sizeof(PeoInfo));if (pc->data == NULL){perror("InitContact:");return;}pc->capacity = 2;LoadContact(pc);/*memset(pc->data, 0, sizeof(pc->data));*/
}
void DisplayContact(contact* pc)//打印信息
{assert(pc);printf("%-15s\t%-5s\t%-5s\t%-12s\t%-30s\n", "姓名", "年纪","性别", "电话", "地址");//默认右对齐,修改为左对齐//中间也要留下足够的空间for (int i = 0; i < pc->sz; i++){printf("%-15s\t%-5d\t%-5s\t%-12s\t%-30s\n", pc->data[i].name,pc->data[i].age,pc->data[i].sex,pc->data[i].tele,pc->data[i].addr);}
}void AddContact(contact* pc)//增加联系人
{assert(pc);CheckCapacity(pc);//检查是否满了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);printf("用户添加成功!\n");pc->sz++;
}
int FindName(contact* pc, char name[])
{assert(pc&&name);for (int pos = 0; pos < pc->sz; pos++){if (strcmp(pc->data[pos].name, name) == 0){return pos;}}return -1;
}
void DeleteContact(contact* pc)//删除联系人
{assert(pc);assert(pc->sz >= 0);char name[MAX_NAME];printf("请输入需要删除人的姓名:> ");scanf("%s", name);int pos = FindName(pc, name);if (pos == -1){printf("通讯录中并没有这个人!!\n");return;}for (int i = pos; i < pc->sz - 1; i++){pc->data[i] = pc->data[i + 1];//覆盖}printf("删除成功\n");pc->sz--;
}
void SearchContact(contact* pc)//查找联系人
{assert(pc);char name[MAX_NAME];printf("请输入需要查找人的姓名:> ");scanf("%s", name);int pos = FindName(pc, name);if (pos == -1){printf("通讯录中并没有这个人!!\n");return;}printf("%-15s\t%-5s\t%-5s\t%-12s\t%-30s\n", "姓名", "年纪","性别", "电话", "地址");printf("%-15s\t%-5d\t%-5s\t%-12s\t%-30s", pc->data[pos].name,pc->data[pos].age,pc->data[pos].sex,pc->data[pos].tele,pc->data[pos].addr);
}
void ModifyContact(contact* pc)//修改联系人
{assert(pc);char name[MAX_NAME];printf("请输入需要修改人的姓名:> ");scanf("%s", name);int pos = FindName(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");
}
int cmp1(const void* p1, const void* p2)
{return strcmp(((PeoInfo*)p1)->name, ((PeoInfo*)p2)->name);
}
int cmp2(const void* p1, const void* p2)
{return ((PeoInfo*)p1)->age- ((PeoInfo*)p2)->age;
}
int cmp3(const void* p1, const void* p2)
{return strcmp(((PeoInfo*)p1)->sex, ((PeoInfo*)p2)->sex);
}
int cmp4(const void* p1, const void* p2)
{return strcmp(((PeoInfo*)p1)->tele, ((PeoInfo*)p2)->tele);
}
int cmp5(const void* p1, const void* p2)
{return strcmp(((PeoInfo*)p1)->addr, ((PeoInfo*)p2)->addr);
}
void SortByName(contact* pc)
{qsort(pc, pc->sz, sizeof(PeoInfo), cmp1);
}
void SortByAge(contact* pc)
{qsort(pc, pc->sz, sizeof(PeoInfo), cmp2);
}
void SortBySex(contact* pc)
{qsort(pc, pc->sz, sizeof(PeoInfo), cmp3);
}
void SortByTele(contact* pc)
{qsort(pc, pc->sz, sizeof(PeoInfo), cmp4);
}
void SortByAddr(contact* pc)
{qsort(pc, pc->sz, sizeof(PeoInfo), cmp4);
}
void SortContact(contact* pc)//排序联系人
{assert(pc);printf("请选择如何排序:> ");char input[20] = { 0 };scanf("%s", input);if (strcmp(input, "姓名")==0){SortByName(pc);//按姓名排序}else if (strcmp(input, "年龄") == 0){SortByAge(pc);//按年龄排序}else if (strcmp(input, "性别") == 0){SortBySex(pc);//按性别排序}else if (strcmp(input, "电话") == 0){SortByTele(pc);//按电话排序}else if (strcmp(input, "地址") == 0){SortByAddr(pc);//按地址排序}else{printf("输入非法,请重新输入\n");}
}
void DestroyContact(contact* pc)
{free(pc->data);pc->data= NULL;pc->capacity = 0;pc->sz = 0;printf("销毁成功\n");
}
void SaveContact(contact* pc)//保存数据
{FILE* pfWrite = fopen("contact.txt", "wb");if (pfWrite == NULL){perror("fopen:");return;}//写通讯录中数据到文件中int i = 0;for (i = 0; i < pc->sz; i++){fwrite(&(pc->data[i]), sizeof(PeoInfo), 1, pfWrite);}fclose(pfWrite);pfWrite = NULL;printf("保存成功\n");}

8.3 text.c

#include"contact.h"enum Option//利用枚举增加代码的可读性
{EXIT,//0ADD,DEL,SEARCH,MODIFY,DISPLAY,SORT
};void menu()
{printf("***********************************\n");printf("**    1.add          2.delete    **\n");printf("**    3.search       4.modify    **\n");printf("**    5.display      6.sort      **\n");printf("**             0.exit            **\n");printf("***********************************\n");
}
void test()
{int input = 0;contact con;InitContact(&con);do{menu();printf("请输入你的选择:>");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 DISPLAY:DisplayContact(&con);break;case SORT:SortContact(&con);break;case EXIT:SaveContact(&con);//退出保存数据DestroyContact(&con);break;default:printf("非法输入,请重新输入\n");break;}} while (input);
}int main()
{test();return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/473698.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【python】网络爬虫与信息提取--正则表达式

一、正则表达式 正则表达式是用来简洁表达一组字符串的表达式。是通用的字符串表达框架&#xff0c;简洁表达一组字符串的表达式&#xff0c;针对字符串表达“简洁”和“特征”思想的工具&#xff0c;判断某字符串的特征归属。 用处&#xff1a;表达文本类型的特征&#xff1b;…

AGV命名要求

命名规则&#xff1a; 组代号负载代号导引代号负载方式代号轮系代号升级换代代号(初代可隐藏)变形代号

面试题-01

1、JDK 和 JRE 和 JVM 分别是什么&#xff0c;有什么区别&#xff1f; JDK&#xff08;Java Development Kit&#xff0c;Java 软件开发工具包&#xff09; JDK&#xff08;Java Development Kit&#xff09;&#xff1a;JDK 是 Java 开发⼯具包&#xff0c;包含了编写、编译…

Python爬虫之自动化测试Selenium#7

爬虫专栏&#xff1a;http://t.csdnimg.cn/WfCSx 前言 在前一章中&#xff0c;我们了解了 Ajax 的分析和抓取方式&#xff0c;这其实也是 JavaScript 动态渲染的页面的一种情形&#xff0c;通过直接分析 Ajax&#xff0c;我们仍然可以借助 requests 或 urllib 来实现数据爬取…

数学建模【非线性规划】

一、非线性规划简介 通过分析问题判断是用线性规划还是非线性规划 线性规划&#xff1a;模型中所有的变量都是一次方非线性规划&#xff1a;模型中至少一个变量是非线性 非线性规划在形式上与线性规划非常类似&#xff0c;但在数学上求解却困难很多 线性规划有通用的求解准…

h5和微信小程序实现拍照功能(其中h5暂时无法调用闪光灯)

代码如下 <template><view class"camera"><!-- #ifdef MP --><camera ref"myCamera" id"myCamera" device-position"back" :flash"flash" error"error" style"display: block;"&…

Vue2:组件间通信框架Vuex

一、原理图及作用 功能介绍&#xff1a; 简单说就是Vue项目中&#xff0c;各个组件间通信的一种框架 相较于之前的全局事件总线&#xff0c;该框架更实用&#xff01; 提高了代码的复用率&#xff0c;把核心业务代码&#xff0c;集中到store中&#xff0c;这样&#xff0c;一处…

Java Lambda表达式:简化编程,提高效率

Java Lambda表达式&#xff1a;简化编程&#xff0c;提高效率 1. 使用Lambda表达式进行集合遍历1.1 未使用Lambda表达式&#xff1a;1.2 使用Lambda表达式&#xff1a; 2. 使用Lambda表达式进行排序2.1 未使用Lambda表达式&#xff1a;2.2 使用Lambda表达式&#xff1a; 3. 使用…

市场复盘总结 20240208

仅用于记录当天的市场情况&#xff0c;用于统计交易策略的适用情况&#xff0c;以便程序回测 短线核心&#xff1a;不参与任何级别的调整&#xff0c;采用龙空龙模式 一支股票 10%的时候可以操作&#xff0c; 90%的时间适合空仓等待 二进三&#xff1a; 进级率中 25% 最常用的…

Practical User Research for Enterprise UX

2.1 Why It’s Hard to Get Support for Research in Enterprises 2.1.1 Time and Budget Instead of answering the question “What dowe gain if we do this research?”, ask instead “What do we stand to lose if we don’t do the research?” 2.1.2 Legacy Thinkin…

Mysql第二关之存储引擎

简介 所有关于Mysql数据库优化的介绍仿佛都有存储引擎的身影。本文介绍Mysql常用的有MyISAM存储引擎和Innodb存储引擎&#xff0c;还有常见的索引。 Mysql有两种常见的存储引擎&#xff0c;MyISAM和Innodb&#xff0c;它们各有优劣&#xff0c;经过多次优化和迭代&#xff0c;…

数据结构对链表的初步认识(一)

已经两天没有更新了&#xff0c;今天就写一篇数据结构的链表吧&#xff0c;巩固自己也传授知识&#xff0c;不知道各位是否感兴趣看看这一篇有关联表的文章。 目录 链表的概念与结构 单向链表的实现 链表各个功能函数 首先我在一周前发布了一篇有关顺序表的文章&#xff0c;…