C项目实践之通讯录管理案例

2023-05-16

1.功能需求分析

通讯录管理案例主要实现对联系人的信息进行添加、显示、查找、删除、更新和保存功能。主要功能需求描述如下:

(1)系统主控平台: 充许用户选择想要进行的操作,包括添加联系人信息,显示、查找、删除、更新联系人信息、保存到文件和退出系统。

(2)添加联系人信息: 用户根据提示输入联系人的姓名、性别、电话、手机、传真、地址以及邮编等。输入完一条联系人信息,提示用户是否继续输入下一条联系人信息或者继续其它操作。充许输入多条联系人的信息。输入完的联系人信息暂时保存在单链表中,等待下一步操作。

(3)显示联系人信息:将刚输入的联系人信息从单链表中调出来显示,如果没有数据,则提示无联系人信息。

(4)查找联系人信息:可以根据联系人姓名从单链表中对所有联系人的信息进行查找,如果没有查询到任何信息,则给出提示信息,否则打印找到的信息。

(5)删除联系人信息:根据输入的联系人姓名来查找是否存在该联系人,如果存在则确认是否真的需要删除,确认之后就删除,否则提示没有找到该联系人。

(6)修改联系人信息:根据输入的联系人姓名来查找是否存在该联系人,如果存在则直接调用添加函数对当前节点进行修改操作,实际上这是重新填写联系人的全部信息,而不是修改某一项或几项信息。

(7)文件保存:本模块的功能是将单链表中的信息存储到文本文件中。系统将提示用户输入文本文件名,确认后将单链表中联系人的信息存储到文本文件中。

(8)退出:退出系统。

2.总体设计

2.1功能模块设计

image

1.添加联系人(attnAdd()函数)

首先建立单链表,调用input_person() 函数输入联系人信息,将用户输入的联系人信息存储到单链表中,提示用户是否继续输入,用户输入“Y"或"y",则调用attnAdd()函数实现继续添加联系人操作。用户输入"N"或"n",则返回主菜单界面。(这里的返回主菜单界面实际是在main()中利用while()来做的,而不是在attnAdd()函数中实现返回主菜单界面的)。

2.显示联系人

将单链表中的联系人信息打印出来,单链表不为空时,循环调用print_person()函数打印每个联系人信息。

3.查找联系人

首先提示用户输入要查找的联系人姓名,然后根据输入的信息在单链表中利用strcmp()函数逐个比对,如果发现相同的,则调用print_person()函数显示查找到的联系人信息。如果没有找到则给出提示信息。

4.删除联系人

首先提示用户输入要删除的联系人姓名,再根据输入的姓名在单链表中查找该联系人是否存在,如果存在,调用print_person()函数显示该联系人信息,提示用户是否确认删除该联系人,如果用户输入"Y"或"y", 则删除该联系人。否则提示用户是否继续删除操作,用户输入"Y"或"y”,则再次调用attnDelete()函数进行删除操作。

5.更新联系人

首先查找联系人信息,如果查找不成功,给出提示信息,如果查找成功,先调用print_person()函数显示该联系人信息,再调用input_person()函数输入新的数据。最后提示用户是否继续更新的操作,用户输入"Y"或“y”,则再次调用attnUpdate()函数进行删除操作。

6.保存到文件

首先提示用户输入要保存的文件名,文件将保存在程序目录下。系统将单链表中的信息保存到刚才输入的中文件中,并给出“保存成功”的提示信息。

7.退出系统

将单链表中的数据全部释放掉,防止内存泄露,退出系统。

2.2程序处理流程

系统的执行应从系统菜单的选择开始,充许用户输入1-7之间的数值来选择要进行的操作,输入其它字符都无效的,系统会给出出错的提示信息。若用户输入1,则调用attnAdd()函数进行添加联系人信息操作;输入2,则调用attnShow()函数显示联系人信息;输入3,则调用attnSearch()函数,查找联系人信息;若输入4,则调用attnDelete()函数,删除联系人信息,若输入5,则调用attnUpdate()函数,更新联系人信息,若输入6,则调用attnSave()函数,将联系人信息保存到磁盘文本文件中;若输入7,则调用attnQuit()函数,退出系统。系统的处理流程图如下:

image

3.详细设计与程序实现

首先打开vs ,新建win32 Console Apllication , 选择Empty Proj, 点击Finished 完成项目创建

然后在头文件夹和source文件夹中分别新建AddrMain.h 和 AddrMain.cpp文件!

在要处理的函数比较多,或者说代码量比较长的时候,我们一般会把函数和变量的声明放在.h文件中,而把功能实现放到.cpp中。

首先我们先在AddrMain.h把要用到的函数和变量进行声明,因为待会要对我们自定义的一个全局变量初始化为NULL , 所以先在AddrMain.h的开始处包含头文件:


//Header Information  

#include <iostream>  

AddrBook主要是对联系人的信息进行处理,而联系人信息是由多个数据成员共同构成,因为这些变量在很多地方都要用到,比如main(), attnAdd()等几乎每个函数都要用到这些变量,那么将它们声明为局部变量显示是不行的,但是如果将它们一个个的声明为全局变量,显示也是不理想的,虽然解决了变量全局访问的问题,但是管理起来很麻烦,所以最好的办法是声明一个结构体统一管理这些信息,但是使用结构体需要注意一个问题,那就是结构体所占用的内存大小是它所有成员的长度的总和,注意这里要考虑对齐行为,而不是它们每个成员的长度的简单相加, 为了避免应对齐浪费的空间,我们将所有字段统一声明char类型的数组,那么这里就又有一个问题需要考虑了, 因为我们要声明的字段比较多,那么这些数组的大小是直接在声明的时候给定固定大小呢,还是声明一些宏,然后通过这些宏来维护数组的大小呢? 显然声明宏来维护数组的大小是可取的。那么到现在为止,我们知道为了便于管理,需要声明一个联系人信息的结构体,为了便于维护数组的大小要提前预定义一些常量宏,下面在AddrMain.h中声明用来方便维护数组大小的常量宏:


//Pre-processing Declarations  

#define MAX_NAME 11  

#define MAX_SEX 3  

#define MAX_BIRTHDAY 9  

#define MAX_TEL 21  

#define MAX_MOBILE 21  

#define MAX_FAX 21  

#define MAX_ADDRESS 101  

#define MAX_POSTAL_CODE  7  

接下来定义操作联系人信息的结构体_person ,并用typedef 定义一个新类型person,结构体中包括联系人姓名、性别、出生日期、电话、手机、传真、地址及邮编共8个成员。


//DataType Definition  

typedef struct _person  

{  

    char name[MAX_NAME];                //姓名  

    char sex[MAX_SEX];                  //性别  

    char birthday[MAX_BIRTHDAY];        //出生日期  

    char tel[MAX_TEL];                  //电话号码  

    char mobile[MAX_MOBILE];            //手机号码  

    char fax[MAX_FAX];                  //传真  

    char address[MAX_ADDRESS];          //地址  

    char postal_code[MAX_POSTAL_CODE];  //邮编  

}person;  

好现在联系人信息的结构体有了,我们通过这个person结构体可以方便地对联系人的信息进行操作了  

但是每操作完一个person ,它都只代表某一个联系人的信息,那操作完目前这个联系人信息之后我们怎么去操作下一个指定的联系人信息呢? 这就需要定义一个带有指针域的结构体,通过这个指针域来保存person结构体的内存地址,从而我们就可以把记录了不同的联系人信息的结构体person 链接在一起了。这样也就构成了一个单链表。因此,我们还需要声明一个单链表结构体类型 _addr_book, 用typedef 定义一个新类型addr_book, 在这个结构体中包括了一个存储联系人基本信息的结构体变量per 和指向下一个联系人的指针变量。具体声明如下:


typedef struct _addr_book  

{  

    person per; // 联系人基本信息  

    struct _addr_book* next;  

}addr_book;  

addr_book是构成单链表的一个单元元素,而单链表在没有任何值的情况下是空链表,那么它的头指针应该初始化为空:

addr_book* first = NULL; //单链表头指针,初始化为空

上面我们把要用到的用于存储联系人信息的结构体person 和用来构建单链表的基本单元add_book都声明好了之后,下面我们就需要实现主要的功能模块,通过前面的功能需求分析,很明显我们要实现的功能有添加联系人信息函数,显示联系人信息函数、查找联系人信息函数,删除联系人函数,更新联系人函数,保存到文件和退出函数8个功能函数。下面是它们对应的函数声明:


//Function and variable Declarations  

bool attnAdd();    //添加联系人信息函数  

void attnShow();   //显示联系人信息函数  

void attnSearch(); //查找联系人函数  

void attnDelete(); //删除联系人函数  

void attnUpdate(); //更新联系人信息函数  

void attnSave();   //保存联系人信息函数  

void attnQuit();   //退出系统函数  

为了使得函数实现更加简洁明了,我们把上面的一些函数可能都要用的功能作为小函数抽象来了,主要包括打印主菜单函数、取链表中的最后一个值的函数、打印联系人信息函数和输入联系人信息函数。它们对应的声明如下:


//Auxiliary Function Declarations  

int print_menu();                     //打印主菜单界面  

addr_book* get_last(addr_book* from); //取得链表最后一个值  

void print_person(person *p);         //显示一个联系人信息  

void input_person(person *p);         //提示输入一个联系人信息  

因为主菜单中包括系统功能的所有选项,但是它只是作为一个显示,为了更加美观化,因此我们特意单独定义一个常量来处理:


//constant  

char menu[] =   

    "+================================================+\n"  

    "|               通讯簿管理系统             |\n"  

    "+------------------------------------------------+\n"  

    "|  1 添加联系人                            |\n"  

    "|  2 显示所有联系人                        |\n"  

    "|  3 查找联系人                            |\n"  

    "|  4 删除联系人                            |\n"  

    "|  5 更新联系人                            |\n"  

    "|  6 保存                                  |\n"  

    "|  7 退出系统                              |\n"  

    "+================================================+\n";  

好了, 到目前为止我们已经把项目中要用到的函数和变量都做了声明,下一步就要到AddrMain.cpp文件中去实现这些功能,然后调试运行就可以了。

下面打开AddrMain.cpp, 首先把要用到的头文件包含进来:


#include <cstdlib> //标准函数库, 因为要用到strcpy()做字符串比较处理  

#include <iomanip> //这里要用到setw()设置宽度显示  

#include "AddrMain.h"  

   

using namespace std;  

首先来实现主函数main()

主函数中, 首先调用打印主菜单界面函数print_menu(), 打印主菜单,等待用户输入1-7中的任一数值,根据用户的输入在switch语句中选择相应的操作,如果输入的不是1-7之间的值则给出提示,并要求重新输入。在这里我们利用了while()语句来实现循环功能也就是说只要用户不输入7,则while()就循环执行一次switch()中的print_menu()函数显示主菜单界面,供用户选择不同的操作! 实现代码如下:


int main()  

{  

    int flg = 1;  

    while(flg)  

    {  

        switch(print_menu()) //打印主菜单界面  

        {  

        case 1:  

            attnAdd();       //提示添加联系人信息  

            break;  

        case 2:  

            attnShow();      //显示联系人信息  

            break;  

        case 3:  

            attnSearch();    //查找联系人信息  

            break;  

        case 4:  

            attnDelete();    //删除联系人信息  

            break;  

        case 5:  

            attnUpdate();    //更新联系人信息  

            break;  

        case 6:  

            attnSave();      //保存联系人信息  

            break;  

        case 7:  

            attnQuit();      //退出系统  

            break;  

        }  

    }  

    return (0);  

}  
下面来实现主要的函数功能
1.添加新记录
函数名称:attnAdd()
函数功能:用户在主菜单中选择1调用此函数,用来输入联系人的基本信息。
处理过程:
(1)首先创建一个结构体指针变量 new_addr, 并将其next指针置NULL,其余信息使用memset函数置0.(注意memset函数是针对字符串处理的函数,应用到其它数据类型可能会得到错误结果)
(2)判断单链表是否有数据,如果没有,即first == NULL , 则置new_addr为头节点,否则调用函数get_last 找到单链表中的最后一个节点last, 将new_addr连接到最后一个节点last的后面。
(3)调用函数input_person完成一个联系人信息的输入操作。
(4)提示用户是否继续输入联系人信息,如果用户输入“y"或"Y", 表示继续输入,再次调用此函数,否则返回主函数(注意这里的返回主函数,其实在attnAdd()里面是什么都没有做,也就是说如果用户输入的不是'y'或'Y',则不做处理,直接退出了attnAdd(),这样就回到了主函数main()中的while()循环中,然while判断此时输入的字符不是1-7之间的数字,它就会循环到switch()中的print_menu(),执行print_menu()就到了等待用户选择操作的主菜单界面)具体实现如下:

bool attnAdd()  

{  

    char input = 'N'; //声明最后的输入标识,并初始化为 N 表示用户不再继续调用此函数  

    addr_book* last = NULL;  //声明一个last指针,用来指向单链表中的最后一个元素, 初始化为NULL   

     // 创建一个新的结构体变量new_addr用来存放新添加联系人的基本信息,为其分配内存  

    addr_book* new_addr = (addr_book*)malloc(sizeof(addr_book));  

   

    if(NULL == new_addr) return false; //如果new_addr内存分配失败,则返回  

    memset(new_addr,0,sizeof(addr_book));//将new_addr中的前addr_book个长度的初值设置为0  

    new_addr->next = NULL;  

      

    if(NULL == first)  

    {  

        first = new_addr;  

    }  

    else  

    {  

        last = get_last(first);  

        last->next = new_addr;  

    }  

    input_person(&(new_addr->per));  

      

    cout<<">继续输入?(Y 继续, N 返回菜单)"<<endl;  

    getchar();  

    input = getchar();  

    if('Y' == input || 'y' == input)  

    {  

        attnAdd();  

    }  

    return true;  

}  

2.显示联系人信息

函数名称:attnShow()

函数功能:选择操作2时调用此函数,用来显示联系人的基本信息。

处理过程:

(1)首先定义一个指针变量p指向头节点first。

(2)在单链表未结束时(p != NULL)反复调用print_person函数,逐个打印联系人信息。

(3)如果单链表中没有数据,则给出提示信息。

(4)按任意键返回主菜单,返回主菜单。具体实现如下:


void attnShow()  

{  

    int i = 0;   

    addr_book* p = first;  

    while (NULL != p)  

    {  

        i++;  

        cout<<"****** 第"<<setw(2)<<i<<" 个联系人 ************************"<<endl;  

        print_person(&(p->per));  

        p = p->next;  

    }  

    if( 0 == i)  

    {  

        cout<<"没有联系人!"<<endl;  

    }  

    cout<<"按任意键返回菜单....."<<endl;  

    fflush(stdin);  

    getchar();  

}  

3.查找联系人信息

函数名称:attnSearch()

函数功能:选择操作3调用此函数,根据联系人姓名查找相关信息。

处理过程:

(1)首先定义一个指针变量p指向头节点first;

(2)输入要查找的联系人姓名,根据输入的姓名在单链表中逐个查找,如果找到就调用print_person函数显示该联系人信息,否则给出提示信息。

(3)提示用户是否继续查找,如果用户输入"y”或“Y”,则调用attnSearch()继续进行下一个联系人信息的查找,否则返回主菜单界面。具体实现如下:


void attnSearch()  

{  

    int count = 0;  

    char input = 'N';  

    char name[MAX_NAME] = {0};  

    addr_book* p = first;  

    cout<<">请输入要查找的联系人姓名(最大 "<<setw(2)<<MAX_NAME-1<<" 个字符):";  

    cin>>name;  

    while(NULL != p)  

    {  

        if(strcmp(p->per.name, name) == 0) // 对比输入的字符串和单链表中的某个字符串是否相等  

        {  

            print_person(&(p->per));  

            count++;  

        }  

        p = p->next;  

    }  

    if(0 == count)  

        cout<<"没有找到姓名为"<<setw(2)<<name<<"的人."<<endl;  

    cout<<"继续查找吗?(Y 继续查找, N 返回菜单)"<<endl;  

    getchar();  

    input = getchar();  

    if('Y'== input || 'y' == input)  

    {  

        attnSearch();  

    }  

}  

4.删除联系人信息

函数名称:attnDelete()

函数功能:选择操作4调用此函数,根据联系人姓名删除该联系人信息。

处理过程:

(1)首先定义一个指针变量p指向头节点first,在定义一个指针变量p1,将其置空。

(2)输入要删除的联系人姓名,根据姓名在单链表中逐个查找联系人信息。如果没有找到,则给出提示信息,否则进行删除。

i.如果要删除的节点p是头节点,则将p->next赋值给first, 即first = p->next,直接删除

ii.如果要删除的节点不是头节点,先设置p1指向头节点,然后在循环体中判断p1的后继节点是不是p,借助p1删除节点p, 即p1->next = p->next.

iii。释放节点p.

(3)提示用户是否继续进行删除操作。如果用户输入"y"或"Y",则再次调用attnDelete()函数,进行相应处理,否则返回主菜单界面。具体实现如下:


void attnDelete()  

{  

    int count = 0;  

    char input = 'N';  

    char name[MAX_NAME] = {0};  

    addr_book* p = first;  

    addr_book* pl = NULL;  

    cout<<">请输入要删除的联系人姓名(最大 "<<setw(2)<<MAX_NAME -1<<"个字符):";  

    cin>>name;  

    while(p != NULL)  

    {  

        if(strcmp(p->per.name, name) == 0)  

        {  

            print_person(&(p->per));  

            count++;  

            break;  

        }  

        p = p->next;  

    }  

    if(0 == count)  

    {  

        cout<<"没有姓名为"<<setw(2)<<name<<"的人."<<endl;  

    }  

    else  

    {  

        cout<<"确定要删除姓名为["<<setw(2)<<name<<"]的联系人吗?(Y 确认, N 取消)"<<endl;  

        getchar();  

        input = getchar();  

        if('Y' == input || 'y' == input)  

        {  

            if(first == p)  

            {  

                first = p->next;  

            }  

            else  

            {  

                pl = first;  

                while (pl != NULL)  

                {  

                    if(pl->next == p)  

                    {  

                        pl->next = p->next;  

                        break;  

                    }  

                    pl = pl->next;  

                }  

            }  

            free(p);  

        }  

    }  

    cout<<"继续删除其它联系人吗?(Y 继续删除, N返回菜单)"<<endl;  

    getchar();  

    input = getchar();  

    if('Y' == input || 'y' == input)  

    {  

        attnDelete();  

    }  

}  

5.更新联系人信息

函数名称:attnUpdate()

函数功能:选择操作5就调用此函数,输入联系人姓名,更新其相关信息。

处理过程:

(1)首先定义一个指针变量p,指向头节点first.

(2)输入要更新的联系人姓名,根据姓名在目前单链表中查找。如果找到,则先调用print_person函数显示该联系人信息,然后将当前节点地址作为参数传给input_person,调用input_person函数重新输入该联系人的信息进行更新操作;如果没有输入的联系人则给出提示信息。

(3)提示用户是否继续更新的操作,

如果用户输入“y”或"Y",则再次调用attnUpdat()函数进行处理,否则返回主菜单界面。具体实现如下:


void attnUpdate()  

{  

    int count = 0;  

    char input = 'N';  

    char name[MAX_NAME] = {0};  

    addr_book* p = first;  

    cout<<"请输入要更新的联系人姓名(最大 "<<setw(2)<<MAX_NAME-1<<"个字符):";  

    cin>>name;  

    while(NULL != p)  

    {  

        if(strcmp(p->per.name, name) == 0)  

        {  

            print_person(&(p->per));  

            count++;  

            break;  

        }  

        p = p->next;  

    }  

    if(count == 0)  

    {  

        cout<<"没有找到姓名为 "<<setw(2)<<name<<"的人."<<endl;  

    }  

    else  

    {  

        input_person(&(p->per));  

    }  

    cout<<"继续更新其它联系人吗?(Y 继续更新, N 返回菜单)"<<endl;  

    getchar();  

    input = getchar();  

    if('Y' == input || 'y' == input)  

    {  

        attnUpdate();  

    }  

}  

6.保存联系人信息

函数名称:attnSave()

函数功能:选择操作6调用此函数,将单链表中的信息保存到磁盘文本文件中。

处理过程:

(1)首先定义一个指针变量p, 指向头节点first.

(2) 输入文件名(把后缀一起输入, 如save.txt)在循环题体中利用函数fprintf()逐个将单链表中的联系人信息保存到文本文件中。

(3)返回主菜单。具体实现如下:


void attnSave()  

{  

    FILE *fp;  

    char file[100];  

    addr_book* p = first;  

    cout<<"请输入文件名:"<<endl;  

    cin>>file;  

    fp = fopen(file,"w");  

    while(NULL != p)  

    {  

        fprintf(fp,"%s,%s,%s,%s,%s,%s,%s,%s\n",  

            p->per.name,p->per.sex,p->per.birthday,p->per.tel,  

            p->per.mobile,p->per.fax,p->per.address,p->per.postal_code);  

        p = p->next;  

    }  

    fclose(fp);  

    cout<<"保存成功!\n按任意键返回菜单..."<<endl;  

    fflush(stdin);  

    getchar();  

}  

7.退出系统

函数名称:attnQuit()

函数功能:选择操作7调用此函数, 直接退出系统。

处理过程:

(1)首先定义一个指针变量pdel 指向头节点,在定义一个指针变量p 置空;

(2)判断pedl的next是否为空,如果不为空则表示有下一条数据,此时需要通过一个循环体先将p指向pdel,释放掉pdel,再将p重复赋值给pdel,来循环删除所有的数据,直到pedl的next域为NULL 为止,然后再释放掉pdel,即可。具体实现如下:


void attnQuit()  

{  

    addr_book* pdel = first;  

    addr_book* p = NULL;  

    if(pdel == NULL)  

    {  

        exit(0);  

    }  

   

    while(pdel->next != NULL)  

    {  

        p = pdel->next;  

        free(pdel);  

        pdel = p;  

    }  

    free(pdel);  

    exit(0);  

}  

上面完成了主要功能函数的实现,但是前面我们把上面的2个或多个函数可能需要共同用到的函数作为辅助函数抽象出来了,下面我们对它们逐一进行实现:

1显示主菜单界面

函数名称:print_menu()

函数功能:显示主菜单界面

处理过程:

等待用户输入1-7中的任一数值,如果输入的是1-7中的某一数值则调用相应的函数进行处理,否则给出提示信息。在每次进入打印主界面之前,都会先调用system("cls")函数对控制台清屏操作。具体实现如下:


int print_menu()  

{  

    int selected = 0;  

    system("cls");  

    cout<<menu<<endl;  

    fflush(stdin); //清空缓冲区中的字符  

    cout<<">请选择[1-7]:";  

    cin>>selected;  

    if(selected<1 || selected > 7)  

    {  

        cout<<"错误选择!(请输入1-7),按任意键继续...."<<endl;  

        getchar();  

        getchar();  

    }  

    return selected;  

}  

2.输入一个联系人信息

函数名称:input_person()

函数功能:提示用户输入联系人的基本信息;具体实现如下:


void input_person(person* p)  

{  

    cout<<"请输入联系人信息\n";  

    cout<<"请输入姓名(最大长度 "<<setw(2)<<MAX_NAME-1<<"个字符):";  

    cin>>p->name;  

   

    cout<<"请输入性别(最大长度 "<<setw(2)<<MAX_SEX - 1<<"个字符):";  

    cin>>p->sex;  

      

    cout<<"请输入出生日期(最大长度为 "<<setw(2)<<MAX_BIRTHDAY-1<<"个字符):";  

    cin>>p->birthday;  

      

    cout<<"请输入电话(最大长度为 "<<setw(2)<<MAX_TEL -1<<"个字符):";  

    cin>>p->tel;  

   

    cout<<"请输入手机(最大长度 "<<setw(2)<<MAX_MOBILE-1<<"个字符):";  

    cin>>p->mobile;  

      

    cout<<"请输入传真(最大长度为 "<<setw(2)<<MAX_FAX-1<<"个字符):";  

    cin>>p->fax;  

   

    cout<<"请输入地址(最大长度为"<<setw(2)<<MAX_ADDRESS-1<<"个字符):";  

    cin>>p->address;  

   

    cout<<"请输入邮编(最大长度为 "<<setw(2)<<MAX_POSTAL_CODE -1<<"个字符):";  

    cin>>p->postal_code;  

}  

3.显示一个联系人信息

函数名称:print_person()

函数功能:显示联系人信息,具体实现如下:


void print_person(person* p)  

{  

      

    cout<<"姓名:"<<setw(1)<<p->name<<" 性别:"<<setw(1)<<p->sex<<" 生日:"<<p->birthday<<endl;  

    cout<<"电话:"<<setw(1)<<p->tel<<endl;  

    cout<<"手机: "<<setw(1)<<p->mobile<<endl;  

    cout<<"传真:"<<setw(1)<<p->fax<<endl;  

    cout<<"地址:"<<setw(1)<<p->address<<endl;  

    cout<<"邮编:"<<setw(1)<<p->postal_code<<endl;  

       

}  

4.取得链表中最后一个节点指针

函数名称:get_last()

函数功能:参数传过来一个节点from , 如果from不为空,则顺序找该链表的最后一个节点,如果为空,则from就是最后一个节点,最后返回最后节点的指针。具体实现如下:


addr_book* get_last(addr_book* from)  

{  

    addr_book* p = from;  

    while(NULL != p->next)  

    {  

        p = p->next;  

    }  

    return p;  

}  

好了现在整个系统实现完毕,F5运行,下面是测试结果:

1.主菜单界面

F5系统运行,进入主菜单界面:

image

2.添加联系人

选择操作1,进入添加联系人界面,用户可以根据提示信息完成联系人信息的添加操作,输入完一条信息之后,系统会提示用户是否继续输入下一条联系人信息,如果用户输入"y"或"Y"则继续调用attnAdd()进行相关操作,否则返回主界面:

image

这里要注意:性别那里是最大长度是2个字符,所以如果输入Male就会出错,因为数组越界了,输入'男' 则刚好占用这个两个字符,没有引起数组越界,所以不会出错,下面的生日也是一样,总共只能输入8个字符,如果输入1990-01-10 则会出错,因为造成了数组越界, 那么如果要充许输入Male 或 1990-01-10不会出错,则只需要修改MAX_SEX和 MAX_BIRTHDAY这两个宏的值即可。

3.显示联系人信息

image

4.查找联系人信息

image

5.更新联系人信息

image

6.保存联系人

image

7.删除联系人

image

8退出系统

转载于:https://www.cnblogs.com/AI-Algorithms/p/3385531.html

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C项目实践之通讯录管理案例 的相关文章

  • 自动化运维为什么是必须的?

    2019独角兽企业重金招聘Python工程师标准 gt gt gt 运维团队负责最大限度提高效率 降低成本 xff0c 这也意味着他们往往承受着巨大的压力 xff0c 需要解决在不增加员工的情况下 xff0c 最大限度产出价值的问题 达成这
  • 如何理解事件溯源

    在近期举行的PHPDublin见面会上 xff0c 来自DynamicRes的架构师Barry Sullivan被问到 什么是事件溯源 xff0c 作为对这个问题的回答 xff0c 他在博客上写下了这篇文章 xff0c 详细解释了什么是事件
  • C语言 · 删除数组中的0元素

    算法提高 6 9删除数组中的0元素 时间限制 xff1a 1 0s 内存限制 xff1a 512 0MB 编写函数CompactIntegers xff0c 删除数组中所有值为0的元素 xff0c 其后元素向数组首端移动 注意 xff0c
  • 解决com.intellij.openapi.project.IndexNotReadyException: Please change caller according to com.intelli...

    File gt Invalidate Cache and Restart
  • PyShark入门(2):FileCapture和LiveCapture模块

    原文地址 xff1a http zodiacg net 2016 07 in 本系列文章译自thePacketGeek的系列文章 原创翻译 xff0c 转载请注明出处 PyShark中进行数据包分析的两个典型方法是使用 FileCaptur
  • 使用ctdb+samba+glusterfs搭建NAS集群系统

    1概述 本文介绍使用开源软件ctdb 43 samba 43 gluster搭建NAS集群系统 1 1 使用的开源软件介绍 1 glusterfs glusterfs是一个开源的分布式文件系统 xff0c 只适用于大文件存储 xff0c 存
  • react中优雅使用svg矢量图

    icon图标可以有很多形式 比如说CSS Sprite 引用字体图标 纯css 简单的icon 等等 优缺点这里不在赘述 xff0c 自行google之 下面就进入正题说说今天的主角svg 1 svg的发展历史 2001年9月4日 xff0
  • 使用自制相机运行 VINS-Mono

    使用自制相机运行VINS Mono 1 相机与IMU标定2 自制相机测试3 运行效果参考资料 1 相机与IMU标定 VINSmono的安装这里就省略了 xff0c 可以参考作者的github网页 2 我所使用的是ZED相机和Xsens IM
  • 标准正态分布变量的累积概率分布函数

    2019独角兽企业重金招聘Python工程师标准 gt gt gt 最近有个期权项目 xff0c 计算理论价时需要使用标准正态分布变量的累积概率分布函数 xff0c excel中可以通过normsdist函数得到该结果 xff0c 但是项目
  • spring boot 中 @ConditionalOnMissingBean和@ConditionalOnBean注解注意事项

    2019独角兽企业重金招聘Python工程师标准 gt gt gt 关于使用 64 Bean注解注入bean导致ConditionOnMissBean和 ConditionOnBean 注解有时候会失效的问题 文档上提示 xff0c 需要注
  • vscode左侧文件不同颜色标识含义

    代码里的左侧颜色标识 红色 xff0c 未加入版本控制 刚clone到本地 绿色 xff0c 已经加入版本控制暂未提交 新增部分 蓝色 xff0c 加入版本控制 xff0c 已提交 xff0c 有改动 xff1b 修改部分 白色 xff0c
  • 读取本地文件转化成MultipartFile

    介绍 现在有个上传文件功能 xff0c 需要将文件上传到oss上 xff0c 但是文件有点多 xff0c 于是使用接口进行上传 但是需要上传文件转换为MultipartFile类型文件进行上传 主要代码 添加pom文件 lt depende
  • 桌面上嵌入窗口(桌面日历)原理探索

    摘要 今天在QQ群里有人问怎样实现将自己的窗口嵌入桌面 xff0c 让它和桌面融为一体 xff0c 就像很多桌面日历软件那样 阅读全文 Richard Wei 2012 05 03 22 07 发表评论 转载于 https www cnbl
  • Git中分支merge和rebase的适用场景及区别

    几乎所有的版本控制工具都有branch功能 xff0c branch主要用于以下几个场景 xff1a 1 xff0c 控制产品OEM 基本上做产品 xff0c 不同的客户都会提出多种不同特性需求 xff0c 最简单的例子就是LOGO和标题完
  • sass安装与教程

    首先下载ruby http dlsw baidu com sw search sp soft ff 22711 rubyinstaller V2 2 2 95 setup 1439890355 exe 安装时注意勾选一下选项 安装完ruby
  • 集成学习原理小结

    集成学习 ensemble learning 可以说是现在非常火爆的机器学习方法了 它本身不是一个单独的机器学习算法 xff0c 而是通过构建并结合多个机器学习器来完成学习任务 也就是我们常说的 博采众长 集成学习可以用于分类问题集成 xf
  • 这款APP明确告诉你,无人机在什么地方可以飞

    美国联邦航空管理局 xff08 FAA xff09 为无人机管制推出了一个新的应用 B4UFLY xff0c 用来向用户显示无人机飞行的合法范围 关于如何有效的对无人机飞行进行管制 xff0c 已经是老生常谈的问题了 xff0c 除了制定相
  • 什么是委托?为什么要使用委托?什么是事件?

    1 什么是委托 xff1f 首先声明一个委托 xff1a public delegate string IsLengthFive string s 下面写几个方法 xff1a public string DoWork string a pu
  • 读取多超声波传感器

    读取多超声波传感器 1 背景2 使用教程2 1 接线说明2 2 上传协议 3 ROS节点使用3 1 下载与配置3 3 常见问题 无串口权限 4 更新程序参考资料 该模块是一个开源模块 xff0c 并提供了配套的ROS节点 xff0c 接收串

随机推荐