1.功能需求分析

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

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

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

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

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

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

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

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

(8)退出:退出系统。

2.总体设计

2.1功能模块设计

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()函数,退出系统。系统的处理流程图如下:

3.详细设计与程序实现

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

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

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

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

//Header Information
#include <iostream>

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

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

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }接下来定义操作联系人信息的结构体_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;

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

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

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

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

typedef struct _addr_book
{
    person per; // 联系人基本信息
    struct _addr_book* next;
}addr_book;

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

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();   //退出系统函数

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }为了使得函数实现更加简洁明了,我们把上面的一些函数可能都要用的功能作为小函数抽象来了,主要包括打印主菜单函数、取链表中的最后一个值的函数、打印联系人信息函数和输入联系人信息函数。它们对应的声明如下:

//Auxiliary Function Declarations
int print_menu();                     //打印主菜单界面
addr_book* get_last(addr_book* from); //取得链表最后一个值
void print_person(person *p);         //显示一个联系人信息
void input_person(person *p);         //提示输入一个联系人信息

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }因为主菜单中包括系统功能的所有选项,但是它只是作为一个显示,为了更加美观化,因此我们特意单独定义一个常量来处理:

//constant
char menu[] = 
    "+================================================+\n"
    "|               通讯簿管理系统             |\n"
    "+------------------------------------------------+\n"
    "|  1 添加联系人                            |\n"
    "|  2 显示所有联系人                        |\n"
    "|  3 查找联系人                            |\n"
    "|  4 删除联系人                            |\n"
    "|  5 更新联系人                            |\n"
    "|  6 保存                                  |\n"
    "|  7 退出系统                              |\n"
    "+================================================+\n";

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }好了, 到目前为止我们已经把项目中要用到的函数和变量都做了声明,下一步就要到AddrMain.cpp文件中去实现这些功能,然后调试运行就可以了。

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

#include <cstdlib> //标准函数库, 因为要用到strcpy()做字符串比较处理
#include <iomanip> //这里要用到setw()设置宽度显示
#include "AddrMain.h"
 
using namespace std;

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

首先来实现主函数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);
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

下面来实现主要的函数功能
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();
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }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();
    }
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

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();
    }
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

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();
    }
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

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();
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

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);
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

上面完成了主要功能函数的实现,但是前面我们把上面的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;
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }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;
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }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;
     
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }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;
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

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

1.主菜单界面

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

2.添加联系人

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

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

3.显示联系人信息

4.查找联系人信息

5.更新联系人信息

6.保存联系人

7.删除联系人

8退出系统

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

  1. 《Spring Boot 入门及前后端分离项目实践》系列介绍

    课程计划 课程地址点这里 本课程是一个 Spring Boot 技术栈的实战类课程,课程共分为 3 个部分,前面两个部分为基础环境准备和相关概念介绍,第三个部分是 Spring Boot 项目实践开发 ...

  2. 《Spring Boot 入门及前后端分离项目实践》目录

    开篇词:SpringBoot入门及前后端分离项目实践导读 第02课:快速认识 Spring Boot 技术栈 第03课:开发环境搭建 第04课:快速构建 Spring Boot 应用 第05课:Spr ...

  3. Django项目实践4 - Django网站管理(后台管理员)

    http://blog.csdn.net/pipisorry/article/details/45079751 上篇:Django项目实践3 - Django模型 Introduction 对于某一类 ...

  4. Django项目实践4 - Django站点管理(后台管理员)

    http://blog.csdn.net/pipisorry/article/details/45079751 上篇:Django项目实践3 - Django模型 Introduction 对于某一类 ...

  5. [转]Asp.Net大型项目实践(11)-基于MVC Action粒度的权限管理【续】【源码在这里】(在线demo,全部源码)

    本文转自:http://www.cnblogs.com/legendxian/archive/2010/01/25/1655551.html 接上篇Asp.Net大型项目实践(10)-基于MVC Ac ...

  6. Hangfire项目实践分享

    Hangfire项目实践分享 目录 Hangfire项目实践分享 目录 什么是Hangfire Hangfire基础 基于队列的任务处理(Fire-and-forget jobs) 延迟任务执行(De ...

  7. Hangfire项目实践

    Hangfire项目实践分享 Hangfire项目实践分享 目录 Hangfire项目实践分享 目录 什么是Hangfire Hangfire基础 基于队列的任务处理(Fire-and-forget ...

  8. Spring cloud项目实践(一)

    链接地址:http://sail-y.github.io/2016/03/21/Spring-cloud%E9%A1%B9%E7%9B%AE%E5%AE%9E%E8%B7%B5/ 什么是Spring ...

  9. 【微信小程序项目实践总结】30分钟从陌生到熟悉 web app 、native app、hybrid app比较 30分钟ES6从陌生到熟悉 【原创】浅谈内存泄露 HTML5 五子棋 - JS/Canvas 游戏 meta 详解,html5 meta 标签日常设置 C#中回滚TransactionScope的使用方法和原理

    [微信小程序项目实践总结]30分钟从陌生到熟悉 前言 我们之前对小程序做了基本学习: 1. 微信小程序开发07-列表页面怎么做 2. 微信小程序开发06-一个业务页面的完成 3. 微信小程序开发05- ...

随机推荐

  1. python之多线程与多进程

    1. 多进程与多线程 (1)背景:为何需要多进程或者多线程:在同一时间里,同一个计算机系统中如果允许两个或者两个以上的进程处于运行状态,这便是多任务.多任务会带来的好处例如用户边听歌.边上网.边打印, ...

  2. 转载 js弹出框、对话框、提示框、弹窗总结

    转载:https://blog.csdn.net/huileiforever/article/details/9464659 一.JS的三种最常见的对话框   //================== ...

  3. LeetCode(66)Plus One

    题目 Given a non-negative number represented as an array of digits, plus one to the number. The digits ...

  4. 利用nginx设置浏览器协商缓存

    强缓存与协商缓存的区别 强缓存:浏览器不与服务端协商直接取浏览器缓存 协商缓存:浏览器会先向服务器确认资源的有效性后才决定是从缓存中取资源还是重新获取资源 协商缓存运作原理 现在有一个这样的业务情景: ...

  5. Spring拓展接口之BeanPostProcessor,我们来看看它的底层实现

    前言 开心一刻 小明:“妈,我被公司开除了”,妈:“啊,为什么呀?”, 小明:“我骂董事长是笨蛋,公司召开高层会议还要起诉我”,妈:“告你诽谤是吧?”,小明:“不是,他们说要告我泄露公司机密” Bea ...

  6. js的几个可能不清晰的问题

    一、关于全局变量的 var test=function(){ var a=1; setTimeout(function(){ console.log(a); a=2; },1000); a=3; se ...

  7. sql的case when用法

    select t.C_OPERATE_TIME MODIFY_TIME, t.c_code EMPLOYEE_CODE, t.c_name EMPLOYEE_NAME, CASE t.c_employ ...

  8. linux shell管道和xargs的区别

    如上图,加了xargs的话相当于将上一个操作的结果作为命令执行前的操作,不加的话直接先把后面的命令运行一遍再操作

  9. zoj 1251 Box of Bricks

    Box of Bricks Time Limit: 2 Seconds      Memory Limit: 65536 KB Little Bob likes playing with his bo ...

  10. Codeforces225E - Unsolvable

    Portal Description 求所有对于方程\[z=\left \lfloor \frac{x}{2} \right \rfloor+y+xy\]不存在正整数解\((x,y)\)的\(z\)中 ...