C项目实践--家庭财务管理系统

1.功能需求分析

家庭财务管理系统给家庭成员提供了一个管理家庭财务的平台,系统可以对家庭成员的收入和支出进行增加,删除、修改和查询等操作,并能统计总收入和总支出。其主要功能需求描述如下:

(1)系统主菜单界面: 充许用户选择想要进行的操作,包括输入收入管理、支出管理、统计和退出系统等操作。其中收入管理包括添加收入、查询收入明细、删除收入和修改收入的操作,支出管理包括添加支出、查询支出明细、删除支出和修改支出的操作。统计是对总收入和总支出进行统计操作。

(2)添加收入处理: 用户根据提示,输入要添加的收入信息,包括收入的日期,添加收入的家庭成员姓名、收入的金额以及备注信息。输入完一条收入记录,将其暂时保存在单链表中,然后返回到主菜单。

(3)查询收入明细处理:根据用户输入的年月信息在单链表中查找收入信息,如果查询成功,按照预定格式显示该收入明细。如果没有该数据,则给出提示信息。查询结束后,询问用户是否继续查找,根据用户的输入进行下一步操作。

(4)删除收入处理:首先提示用户输入要删除的信息的年月,根据用户的输入在单链表中查询。如果没有查到任何信息,则给出提示信息。如果查询成功,显示该收入明显,并提示用户输入要删除的信息的对应序号,以删除该信息,用户输入对应的序号就删除相关的收入信息,并给出删除成功的提示信息,用户输入其它按键则重新删除操作。

(5)修改收入处理:首先提示用户输入要进行修改的信息的年月,如果单链表中有该收入信息存在,则提示用户输入要修改的收入日期,家庭成员姓名等信息并将结果重新存储到单链表中。如果没有找到要修改的收入信息,则给出提示信息。

(6)添加支出处理 完成用户支出信息的添加,与添加收入处理类似。

(7)查询支出明细处理 查询支出信息,与查询收入明细信息类似。

(8)删除支出处理,删除支出信息,与删除收入信息处理类似。

(9)修改支出处理,修改支出信息与修改收入信息类似。

(10)统计总收入和总支出处理: 计算单链表中所有的收入总和和支出总和,并计算出家庭收入的结余信息

(11)退出系统。

2.总体设计

2.1功能模块设计

image

1.添加收入信息

主菜单中选择1 ,进入添加收入操作,系统调用add_income()函数来添加收入信息,首先建立单链表,函数调用input_info()函数提示用户输入收入信息,并存储到单链表中,输入完成后返回到主菜单界面。

2.查询收入明细

主菜单中选择2, 进入查询收入明细操作,系统调用search_income()函数来查询收入的明细,调用search_data()函数来完成具体的查询操作。首先提示用户输入要查询的年月,如果用户输入错误,则给出输入有误提示信息,如果输入正确,在单链表中查找该年月的收入信息,如果查找成功,按照指定格式显示找到的收入信息。每页显示9条收入信息,如果找到收入的信息多于9条,按空格键翻页。如果没有找到任何信息,则给出相应的提示信息。查找成功后提示用户是否继续查找操作,如果用户输入"y"或"Y“, 则再次调用次函数进行相应的操作。否则,直接返回主菜单界面。

3.删除收入信息

主菜单中选择3的时候进行删除收入信息的操作,调用delete_data()函数进行删除收入的操作。首先提示用户输入要删除的信息的所属年月,然后根据输入的年月信息在单链表中查询相关信息,如果查找成功则调用show_info()函数显示查找到的收入信息,每页最多显示9条信息,如果查找到的收入信息多于9条,则按空格键翻页,最后提示用户输入要删除的收入信息的序号,完成删除的操作。如果查找不成功,给出相应的提示信息。删除操作结束后,提示用户是否继续删除的操作,如果用户输入"y"或"Y",则再次调用此函数进行相应的操作,否则返回主菜单界面。

4.更新收入信息

主菜单中选择4,进入更新收入信息的操作,调用update_data()函数进行更新收入信息的操作。首先,提示用户输入要更新收入所属年月,然后根据用户输入的年月信息在单链表中查找相关信息。如果查找到的收入信息多于9条,则按空格键翻页,最后提示用户输入要更新的收入信息的序号,调用inut_info()函数,输入要更新的收入信息,完成更新的操作。如果查找不成功,则给出相应的提示信息。更新操作结束后,提示用户是否继续进行更新操作,如果用户输入"y"或"Y", 则再次调用本函数进行更新操作,否则,返回主菜单界面。

5.添加支出信息

主菜单中选5, 进入添加支出信息操作。系统调用add_income()函数来添加支出信息。添加支出信息的操作和添加收入信息的操作相似。

6.查询支出明细

主菜单中选6,进入查询支出明细操作,系统调用search_income()函数来查询支出明细操作。调用search_data()函数来完成具体的查询操作。查询支出明细的操作与查询收入明细的操作相似。

7.删除支出信息

主菜单选择7,进入删除支出信息操作,调用delete_data()函数进行删除支出的操作。删除支出操作与删除收入操作相似。

8.更新支出信息

主菜单中选择8,进入更新支出信息操作。调用update_data()函数进行更新支出信息操作。具体操作过程与更新收入信息操作相似。

9.统计总收入,总支出

主菜单中选择9,进入统计操作。调用count_total()函数进行总收入,总支出的统计。在单链表中,计算收入和支出的总和,并将两者相减得到家庭收入的结余,并按相应格式显示出来。统计结束之后,按任意键返回主菜单界面。

10.退出系统

主菜单中选择10,进入退出系统操作。调用quit()函数进行退出操作,首先调用save_to_file()函数,将单链表中的数据保存到文件,再调用clear_data()函数清空单链表,最后退出系统。

2.2系统处理流程

image

系统执行应从主菜单的选择开始,充许用户输入0-9之间的数值来选择要进行的操作,输入其它字符都是无效的,系统会给出出错的提示信息。若用户输入0,调用quit()函数退出系统;

若输入1,则调用add_income()函数,进行添加收入操作;

若输入2,则调用search_income()函数,进行查询收入明细;

若输入3,则调用delete_income()函数,进行删除收入操作;

若输入4,则调用update_income()函数,进行修改收入操作;

若输入5,则调用add_payout()函数,进行添加支出操作;

若输入6,则调用search_payout()函数,进行查询支出明显;

若输入7,则调用delete_payout()函数,进行删除支出操作;

若输入8,则调用update_payout()函数,进行更新支出操作;

若输入9,则调用count_total()函数,进行统计总收入,总支出操作。

1.添加操作

首先建立单链表,调用input_info()函数,提示用户输入要添加的收入或支出信息,并将输入信息存储到单链表中,输入完成之后返回主菜单界面。

2.查询操作

首先提示用户输入要查询的年月,如果用户输入错误,给出相应的提示信息;如果用户输入正确,则根据输入的数据在单链表中查找收入或支出的信息。如果没有找到,则给出没有数据的提示信息,如果找到,则判断记录个数是否小于9,如果小于9,直接显示结果,否则提示用户按空格键翻页。查询操作的处理流程图如下所示:

image

3.删除操作

首先提示用户输入要删除的年月,如果用户输入错误,给出相应的提示信息,如果输入正确,则在单链表中查询收入或支出的信息,如果没有找到,给出没有数据的提示信息,如果找到,显示找到的记录信息,并提示用户输入对应的序号进行删除操作。删除成功之后,给出提示信息。删除操作的处理流程如图:

image

4.修改操作

首先提示用户输入要查询的年月,如果输入错误,给出相应的提示信息,如果输入正确,则先在单链表中查找收入或支出的信息,如果没找到,则给出提示信息,否则显示找到的记录信息,并提示用户输入序号,进行修改,用户可以根据提示信息输入要修改的收入或支出信息。修改操作的处理流程如图:

image

2.3详细设计与系统实现

打开vs, 新建win32 console Application ,选择Empty Proj , 点击Finished 完成FinanceMng项目创建

在Header Files 和 Source Files 分别新建 FinanceMain.h 和 FinanceMain.cpp 文件,为了便于维护和逻辑清晰,我们把函数及变量的声明部分放在FinanceMain.h中进行,在FinanceMain.cpp中实现相应的功能模块。下面进入FinanceMain.h文件对系统要用到的函数及变量进行声明,首先包含要用的头文件:

//Header Information
#include <iostream>

家庭财务管理系统主要是对家庭成员的基本信息及财务收支信息的管理,其中涉及到信息我们将它们归类并希望通过自定义三个数据类型来集中管理和使用这些信息,那么其中有些字段由于其本身的需求,最好定义为字符数组来处理,为了具有可扩展性和便于更好的维护,需要先定义一些常量宏,这样当这些字段的大小发生变化时,可以通过简单的修改这些常量宏来达到目的,这其中包括定义 家庭成员姓名的最大长度、收支信息记录的备注最大长度、菜单选项个数以及数据文件名等常量宏的定义,具体定义如下:

//Macro constant 
#define MAX_NAME 11         //家庭成员姓名最大长度
#define MAX_DETAIL 21       // 备注最大长度
#define MENU_COUNT 9        //菜单选项个数
 
#define DATA_FILE "fs.dat"  //数据文件文件名

常量宏定义好之后,接下来就开始来声明三个自定义的数据类型,首先本系统目前主要实现记录家庭成员的支出和收入两大类财务信息,但不确定今后是否需要加入其它类的财务信息的记录,所以最好把它们独立自定义一个数据类型,但是这里需要注意的是,这个数据类型它的成员只有有限的几种值的选择,在目前来看它的值不是代表收入就是支出,无第三种值可以选择, 所以最好定义为一个枚举类型,具体实现如下:

//DataStruct Definition
//自定义枚举类型 fi_type , 用来表示收入支出
typedef enum _fi_type
{
    income = 1,  //收入
    payout = -1  //支出
}fi_type;

接下来定义一个存储家庭财务信息的结构体fi_info

typedef struct _fi_info
{
    int year;               
    int month;
    fi_type type;            //数据类型,即财务类型是收入还是支出
    char name[MAX_NAME];     //家庭成员姓名,属于TA的收入或由TA支出
    float money;             //收入或支出的金额
    char detail[MAX_DETAIL]; //备注信息
}fi_info;

还需要定义一个存储财务数据结构的结构体fi_data, 通过此结构把财务数据连接成链表,具体实现如下:

typedef struct _fi_data
{
    fi_info info;          //财务数据
    struct _fi_data* next; //指向下一个节点的指针
}fi_data;
 
fi_data* head = NULL;  //定义单链表头节点, 初始化为NULL

接下来对本系统要使用到的函数进行声明,首先来声明主菜单对应的处理函数

//Functions Definition
void add_income();     //添加收入
void search_income();  //查询收入明细
void delete_income();  //删除收入
void update_income();  //修改收入
 
void add_payout();     //添加支出
void search_payout();  //查询支出明细
void delete_payout();  //删除支出
void update_payout();  //修改支出
 
void count_total();    //统计总收支情况
void quit();           //退出系统

财务系统中主要进行的操作是进行查询,删除和更新操作,这三个操作在多个功能模块中都将被用到,所以把它们独立声明为函数,供其它函数调用,具体声明如下:

//Main processing Function
void search_data(fi_type type);  //查询处理
void delete_data(fi_type type);  //删除处理
void update_data(fi_type type);  //更新处理

为了使得项目代码更加简洁和逻辑清晰可控,我们把一些非核心的公共部分抽象出来成为系统的辅助函数予以声明处理:

//Auxiliary Function 
void initialize();                      //系统初始化,加载数据
void save_to_file();                    //将财务数据保存到文件中
void clear_data();                      //清空链表中数据
fi_data* get_last(fi_data* p);          //得到财务数据链表的最后一个节点
fi_data* get_previous(fi_data* p);      //取得财务数据节点p的前驱节点
void input_info(fi_info* info);         //输入财务数据信息
void show_info(fi_data* p[],int count); //显示财务数据

接下来将主菜单界面定义为一个常量字符串数组:

//constant
char menu[] = 
"====================================================
"
"| 家庭财务管理系统                            |
"
"+--------------------------------------------------+
"
"| 收入管理                                    |
"
"|      <1> 添加收入                           |
"
"|      <2> 查询收入明细                       |
"
"|      <3> 删除收入                           |
"
"|      <4> 修改收入                           |
"
"| 支出管理                                    |
"
"|      <5> 添加支出                           |
"
"|      <6> 查询支出明细                       |
"
"|      <7> 删除支出                           |
"
"|      <8> 修改支出                           |
"
"| 统计                                        |
"
"|      <9> 统计总收入总支出                   |
"
"+--------------------------------------------------+
"
"| 输入 <0> 退出系统                           |
"
"+--------------------------------------------------+
";
最后为了能够根据用户输入的数值,方便地调用相应的函数进行处理,还需要定义一个函数指针数组用来对应相应的操作函数,具体声明如下:
void (*menu_func[])() =
{
    quit,           //退出系统
    add_income,     //添加收入
    search_income,  //查询收入明细
    delete_income,  //删除收入
    update_income,  //修改收入
    add_payout,     //添加支出
    search_payout,  //查询支出明细
    delete_payout,  //删除支出
    update_payout,  //更新支出
    count_total     //统计收支结余
};

现在基本上已经把要用到的函数和变量在FinanceMain.h文件中进行了声明,如果需求变更则可再来修改即可。下面进入FinanceMain.cpp文件中来实现相应的函数功能,首先应当在FinanceMain.cpp文件开始处包含FinanceMain.h头文件:

#include FinanceMain.h

系统的执行应从主菜单处开始,所以首先来实现main入口函数,将主菜单打印出来,并等待用户输入相应的操作选项,如果输入有误,则给出提示信息,并提示重新输入, 直到输入正确为止。具体实现如下:

//Entry Function 
int main()
{
    //选择标识符,表明用户选择的操作,初始化为-1,表示没有选择任何选项
    int selected = -1;  
    initialize();  //加载数据,初始化系统
    while(1)
    {
        system("cls");//清屏
        printf(menu); //打印主菜单
        printf(">请选择要进行的操作[%d-%d]:",0,MENU_COUNT);
        if(scanf("%d",&selected)!=1 || selected <0 || selected > MENU_COUNT)
        {
            printf(">输入有误!请选择[%d-%d]之间的数字!按任意键继续...",0,MENU_COUNT);
            fflush(stdin);
            getchar();
        }else{
            menu_func[selected]();
        }
    }
    return (0);
}

接下来将对主菜单中对应的处理函数一一进行实现

1.添加收入

函数名称:add_income

函数功能:用来添加收入信息的操作。

处理过程:首先建立单链表,调用input_info()函数,提示用户输入收入信息,并将输入的信息存储到单链表中,输入完成后返回到主菜单界面。具体实现如下:

//添加收入
void add_income()
{
    //定义一个存储财务数据的结构体指针p ,并申请内存
    fi_data* p = (fi_data*)malloc(sizeof(fi_data));
    if(NULL == p) throw printf("内存申请失败...
"); //如果内存申请失败则抛出异常
    memset(p,0,sizeof(fi_data)); // 置空
    p->next = NULL;
    input_info(&(p->info)); //录入相应的财务数据信息
    p->info.type = income;  //财务类型标识为收入
    //如果当前头指针为空,表示当前单链表为空,则把p作为头节点并将地址赋值给head
    if(NULL == head)  
    {
        head = p;
    }else{//否则寻找当前单链表的最后一个节点并把p连接到这个节点后面
        get_last(head)->next = p;
    }
}

因为查询,删除和修改信息在收入管理中的查询收入明细、删除收入信息、更新收入信息这三项操作和支出管理中的查询支出明细、删除支出以及更新支出等三项操作操作几乎相同,仅仅只是财务数据类型不同,所以我们直接把查询、删除和修改信息这三项独立出来,写成公共模块,来供收入管理或支出管理需要使用时作相应的调用即可,即避免代码冗余,亦便于维护。

2.查询收入明细

函数名称:search_income

函数功能:用来查询收入明细的操作。函数中调用search_data()来完成收入明细的查询。具体实现如下:

//查询收入明细
void search_income()
{
    search_data(income);
}

3.删除收入

函数名称:delete_income

函数功能:用来删除收入信息的操作,在函数中调用delete_data()来完成收入信息的删除,具体实现如下:

//删除收入信息
void delete_income()
{
    delete_data(income);
}

4.修改收入

函数名称:update_income

函数功能:用来修改收入信息的操作,通过调用update_data()函数来完成收入信息的修改,具体实现如下:

//更新收入信息
void update_income()
{
    update_data(income);
}

5.添加支出信息

函数名称:add_payout

函数功能:添加支出信息的操作

处理过程:首先建立单链表、调用input_info()函数提示用户输入支出信息,并将输入信息存储在单链表中,输入完成后返回到主菜单界面。实现过程和添加收入雷同,只是录入的财务类型为支出。具体实现如下:

//添加支出信息
void add_payout()
{
    fi_data* p = (fi_data*)malloc(sizeof(fi_data));
    memset(p,0,sizeof(fi_data));
    p->next = NULL;
    input_info(&(p->info));
    p->info.type = payout;
    if(NULL == head)
    {
        head = p;
    }else{
        get_last(head)->next = p;
    }
}

6.查询支出明显

函数名称:search_payout

函数功能:用来查询支出明细操作,通过调用search_data()来实现,具体实现如下:

//查询支出信息明显
void search_payout()
{
    search_data(payout);
}

7.删除支出

函数名称:delete_payout

函数功能:通过调用delete_data()函数来完成删除支出信息的操作,具体实现如下:

//删除支出信息
void delete_payout()
{
    delete_data(payout);
}

8.修改支出

函数名称:update_payout

函数功能:通过调用update_data()来修改支出信息,具体实现如下:

//更新支出信息
void update_payout()
{
    update_data(payout);
}

9.统计总收支结余信息

函数名称:count_total

函数功能:在单链表中,分别计算出收入和支出的总和,并将两者相减算出结余信息,具体实现如下:

//统计总收支结余
void count_total()
{
    float total_income = 0.0;
    float total_payout = 0.0;
    fi_data* p = head;
 
    while(NULL != p)
    {
        if(p->info.type == income)
        {
            total_income += p->info.money;
        }else{
            total_payout += p->info.money;
        }
        p = p->next;
    }
 
    printf("
+------------+------------+------------+
");
    printf("|  合计收入  |  合计支出  |    结余    |
");
    printf("|%12.2f|%12.2f|%12.2f|
",
        total_income,total_payout,total_income - total_payout);
    printf("+------------+------------+------------+
");
    printf(">按任意键返回主菜单...");
    fflush(stdin);
    getchar();
}

10.退出系统

函数名称: quit

函数功能:将单链表中的数据先保存到本地磁盘文件中,然后清空单链表中的数据,最后退出系统。具体实现如下:

//退出系统
void quit()
{
    save_to_file();
    clear_data();
    exit(0);
}

接下来对前面抽象出来的查询、删除和修改三个处理函数进行实现

1.查询处理

函数名称:search_data

函数功能:收入和支出的查询操作。

处理过程:

(1)提示用户按照指定格式输入要查询的年月,如果用户输入错误,则给出提示信息;如果输入正确,则在单链表中查找该年月的信息

(2)如果查找成功,则调用show_info()函数显示找到的信息,并判断查询结果是否小于9条,如果大于9条信息,则提示按空格键进行翻页操作。

(3)如果没有找到符号年月的信息,则给出提示信息

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

//查询处理
void search_data(fi_type type)
{
    int year = 0,  
        month = 0; 
    //定义单链表节点p 
    fi_data* p = NULL;
    //定义单链表 指针数组result,用来记录查询到符合添加的信息的地址
    fi_data* result[9] ={NULL};
    //定义记录查询到的数据条数的变量 count 初始化为0
    int count = 0;
    //定义记录用户输入的选择,是否继续进行查询操作
    char input = ' ' ;
 
    while(1)
    {
        printf(">请输入要查询的年月(例如:2009/1)");  
        fflush(stdin);
        if(scanf("%d/%d",&year,&month) !=2) //*%d%d ->%d/%d
        {
            printf(">输入有误!
");
        }else{
            p = head;//将p指向单链表头节点
            count = 0; //将查询到的记录清零
            //将result 指针清零
            memset(result,0, sizeof(fi_data*));
            while(NULL != p)
            {
                if(p->info.year == year &&
                    p->info.month == month &&
                    p->info.type == type)
                {
                    if(count <9)
                    { //如果当前查询到的记录信息不足9条,则
                        //直接把查询到的记录保存到result指针数组中,
                        //然后count 加1 
                        result[count] = p;
                        count++;
                    }else{
                        //如果查询结果大于9条记录,则先调用show_info()显示这9条信息
                        //并提示用户按空格键进行翻页操作
                        show_info(result,count);
                        printf(">输入空格并回车翻页,按其它键退出....");
                        fflush(stdin);
                        input = getchar();
                        if(input == ' ')
                        {//如果用户输入了空格键,则先清空当前result数组保存的记录地址
                            //然后将记录条数的变量count置0,
                            //把当前查询到的第10条记录先保存到result中,
                            //然后重新开始count加1
                            memset(result , 0, sizeof(fi_data*));
                            count = 0;
                            result[count] = p;
                            count++;
                        }else{
                            //如果用户没有输入空格键进行翻页操作,则直接跳出,返回主界面
                            break;
                        }
                    }//end else
                }//endif 
               //进入下一条记录的比对查找,直到查询完整个单链表为止(即 p == NULL 为止)
                p = p->next;
            }//end while loop
            //如果count 不等0,此时肯定也没有大于9 ,则直接调用show_info()显示信息
            if(0 != count) 
            {
                show_info(result,count);
            }else{
                //count == 0 则提示没有查询到符合添加的数据
                printf(">没有找到数据...
");
            }
            //至此一次完整的查询操作结束, 提示用户是否继续进行查询操作
            printf(">继续查找其它数据?(Y or N)");
            fflush(stdin);
            input = getchar();
            if(input == 'y'||input == 'Y')
            {
                continue;
            }else{
                break;
            }
        }
    }
}

2.删除操作

函数名称:delete_data

函数功能:收入与支出的删除操作

处理流程:

(1)提示用户输入要删除的收支信息所属年月,然后根据用户输入的年月信息在单链表中查找相关信息。

(2)如果查找成功,且查找的记录条数小于9条,则直接调用show_info()函数显示查找到收支信息,如果查找的记录条数大于9,则调用show_info()函数将当前查询到的9条信息显示出来,并提示用户按空格键进行翻页操作。

(3)最后提示用户输入要删除的的收支信息的序号,完成删除操作。

(4)如果查找不成功,则给出相应的提示信息

(5)删除完成之后,提示用户是否继续进行删除操作,如果用户输入"y"或"Y", 则继续进入删除操作界面,否则返回主菜单界面。具体实现如下:

//删除操作
void delete_data(fi_type type)
{
    int year = 0,
        month = 0;
    //定义一个单链表节点p, 用来标识当前节点位置
    fi_data* p = NULL;
    //定义一个单链表节点pre ,用来标识当前节点的前驱节点
    fi_data* pre = NULL;
    //定义一个指针数组用来保存当前查询到的记录信息的地址
    fi_data* result[9] = {NULL};
    //定义一个记录信息的变量,记录当前查询到的符合条件的条数
    int count = 0;
    //用来标识用户输入的选择,是否判断是否继续进行删除操作
    char input = ' ';
    //定义一个变量i,
    int i = 0; 
 
    while(1)
    {
        printf(">请输入要查询的年月(例如:2009/1):");
        fflush(stdin); //清空缓冲区,以免造成死循环!
        if(scanf("%d/%d",&year,&month) != 2) //* %d%d->%d/%d
        {
            printf(">输入有误!
");
        }else{
            p = head; //p指向头节点
            count = 0; //记录信息条数清零 
            //清空result中的记录信息
             memset(result, 0, sizeof(fi_data*));
            while(NULL != p)
            {
                if(p->info.year == year && 
                    p->info.month == month &&
                    p->info.type == type)
                {
                    if(count< 9)
                    {
                        result[count] = p;
                        count++;
                    }else{
                        show_info(result,count);
                  printf(">输入空格并回车翻页.输入对应的序号删除.其它键退出.请输入:");
                        fflush(stdin);
                        input = getchar();
                        if(input == ' ')
                        {//输入空格键,继续查询符合条件的记录,并清空result数组信息,
               //同时将记录条数count归零处理, 并此次查询到的符合条件的信息保存到result中
                            memset(result, 0, sizeof(fi_data*));
                            count = 0; 
                            result[count] = p;
                            count++;
                        }else if(input >= '1' && input <= 48 + count){
                            //用户键入选择删除的记录信息的对应序号
                            i = input -49;
                            //获取当前节点的前驱节点
                            pre = get_previous(result[i]);
                            if(NULL ==pre)//如果前驱节点为空,则表明当前节点为头节点
                            { //删除头节点
                                head = head->next;
                            }else{
                                //否则删除当前节点
                                pre->next = result[i]->next;
                            }
                            //释放掉当前节点所占内存
                            free(result[i]);
                            //将result数组中i节点后面的节点前移动一位
                            for(; i<count -1; i++)
                            {
                                result[i] = result[i+1];
                            }
                            result[i] = p;
                            printf(">删除成功!
");
                        }else{
                            break;
                        }
                    } //end else 
                }//end the first  if 
 
                p = p->next;
            } //end the inner while-loop
            if(0 != count)
            {
                show_info(result,count);
                printf(">输入对应的序号删除.其它键退出.请输入:");
                fflush(stdin);
                input = getchar();
                if(input >= '1' && input <= 48+ count)
                {
                    //修改 input- 48 为 input - 48 -1 因为数组的下标是从0开始的
                    i = input - 48 -1;
                    pre = get_previous(result[i]);
                    if(pre == NULL)
                    {
                        head = head->next;
                    }else{
                        pre->next = result[i]->next;
                    }
                    free(result[i]);
                    for(; i<count-1 ; i++)
                    {
                        result[i] = result[i+1];
                    }
                    result[i] = NULL;
                    count--;
                    printf(">删除成功...
");
                }
            }else{
                printf(">没有找到数据...
");
            }
            printf(">继续查找其它数据?(Y or N)");
            fflush(stdin);
            input = getchar();
            if(input == 'y' || input == 'Y')
            {//重新进行最外面的while循环,继续进行删除操作
                continue;
            }else{
                break;
            }
        }//end else
    }//end while-loop
}

注:上面这段代码中可以将完成具体删除操作的那部分再次独立出来,因为在记录上大于9条和没有大于9这两种情况都要用到,而上面的代码是重复写了这两种操作都需要的具体进行删除的操作。

3.更新处理

函数名称:update_data

函数功能:收支信息的更新操作

处理流程:

(1)首先提示用户输入要更新的信息所属的年月,然后根据此输入进行查询

(2)如果查询成功,且查找的记录条数小于9,则直接调用show_inof()函数显示出来,如果大于9,则提示空格键翻页操作。

(3)提示用户输入要更新的信息的序号,然后调用input_info()函数输入要更新的信息,完成更新操作。

(4)如果查找不成功,则给出提示信息

(5)提示用户是否继续进行更新操作,如果用户输入"y"或'Y", 则继续进行更新操作,否则直接返回主界面

更新操作和删除操作类似, 只是将删除操作中完成具体删除操作的部分替换成调用input_info()来完成更新数据的录入工作。其它几乎是一样的。具体实现如下:

//更新收支信息
void update_data(fi_type type)
{
    int year = 0,
        month = 0;
    fi_data* p = NULL;
    fi_data* pre = NULL;
    fi_data* result[9] = {NULL};
    int count = 0; 
    char input = ' ';
    int i = 0;
 
    while(1)
    {
        printf(">请输入要查询的年月(例如:2009/1):");
        if(scanf("%d/%d",&year,&month) !=2)  //*%d%d -> %d/%d
        {
            printf(">输入有误!...
");
        }else{
            p = head;
            count = 0;
            memset(result, 0, sizeof(fi_data*));
            while(NULL != p)
            {
                if(p->info.year == year 
                    && p->info.month == month
                    && p->info.type == type)
                {
                    if(count <9)
                    {
                        result[count] = p;
                        count++;
                    }else{
                        show_info(result,count);
                 printf(">输入空格并回车翻页. 输入对应的序号修改.其它键退出,请输入:");
                        fflush(stdin);
                        input = getchar();
                        if(input == ' ')
                        {
                            memset(result,0,sizeof(fi_data*));
                            count = 0;
                            result[count] = p;
                            count++;
                        }else if(input >= '1' && input <= 48 + count)
                        {
                            i = input - 49;
                            input_info(&(result[i]->info));
                            printf(">修改成功...
");
                            p = get_previous(p);
                        }else{
                            break;
                        }
                    }
                }
 
                p = p->next;
            }
            if(0 != count)
            {
                show_info(result,count);
                printf(">输入对应的序号修改,其它键退出,请输入:");
                fflush(stdin);
                input = getchar();
                if(input >= '1' && input <= 48 + count)
                {
                    i = input - 49;
                    input_info(&(result[i]->info));
                    show_info(result,count);
                    printf(">修改成功...
");
                }
            }else{
                printf(">没有找到数据...
");
            }
            printf(">继续查找其它数据?(Y or N )");
            fflush(stdin);
            input = getchar();
            if(input == 'y' || input == 'Y')
            {
                continue;
            }else{
                break;
            }
        }
    }
}

上面我们实现了系统的主要功能模块,除此之外,还需要实现一些公共的非和核心的函数,例如系统初始化等,它们的具体实现如下:

1.系统初始化

函数名称:initialize

函数功能:系统初始化操作,包括数据文件和单链表的初始化,具体实现如下:

//系统初始化, 加载数据
void  initialize()
{
    FILE *fp = NULL;
    fi_data* p  = NULL;
    fi_data* last = NULL;
    int count = 0;
    //以只读方式打开一个二进制文件:DATA_FILE文件
    fp = fopen(DATA_FILE, "rb");
    if(NULL == fp)
    {//如果当前文件指针为空,则以只写方式打开DATA_FILE文件
        fp = fopen(DATA_FILE, "w");
        fclose(fp);
        return;
    }
    //定义一个单链表指针,将内存地址置零,并将next指针域置空
    p = (fi_data*)malloc(sizeof(fi_data));
    memset(p,0,sizeof(fi_data));
    p->next = NULL;
    //利用fread将fp中的数据读取到p->info中,每次读取sizeof(fi_info)个字节大小的数据
    //每次只读取1次,如果读取成功则fread返回1 
    while(fread(&(p->info),sizeof(fi_info),1,fp) == 1)
    {
        if(NULL == head)
        {
            head = p;
        }else{
            last = get_last(head);
            last->next = p;
        }
 
        count++;
        //上面通过"rb"方式打开的DATA_FILE, 实质是以二进制形式打开这个文件
        //通过fseek以文件开始位置为文件指针的起始偏移基准位置,每次偏移
        //*sizeof(fi_info)字节大小的位置
        fseek(fp,count *sizeof(fi_info),SEEK_SET);
        //分配内存
        p = (fi_data*)malloc(sizeof(fi_data));
        memset(p,0,sizeof(fi_data));
        p->next = NULL;
    }
    free(p);
    p = NULL;
    fclose(fp);
}

2.将财务数据保存到文件中

函数名称:save_to_file

函数功能:将单链表中的数据保存到文件中。具体实现如下:

//将数据保存到二进制文件中
void save_to_file()
{    //以只写方式打开一个二进制文件
    FILE *fp = fopen(DATA_FILE, "wb");
    //定义一个fi_data* 指针 并指向头节点
    fi_data* p = head;
    while (NULL != p)
    {   // 将&(p->info)地址上的数据内容,写入到fp中
        fwrite(&(p->info),sizeof(fi_info),1,fp);
        //fp偏移,始终将fp位置置为文件末尾
        fseek(fp,0,SEEK_END);
        p = p->next;
    }
    fclose(fp);
}

3.清空链表中的数据

函数名称:clear_data

函数功能:退出系统时调用此函数,清空单链表中的数据。具体实现如下:

//清空单链表中的数据
void clear_data()
{
    fi_data* p = NULL;
    while(NULL != head)
    {
        fi_data* p = NULL;
        //从链表开始处开始清空数据
        if(NULL != head->next)
        {
            p = head;
            head = head->next;
            free(p);
            p = NULL;
        }else{
            free(head);
            head = NULL;
        }
    }
}

4.取得最后一个节点地址

函数名称:get_last

函数功能:取得收支数据链表中的最后一个节点指针。具体实现如下:

//取得最后一个节点,并返回此节点指针
fi_data* get_last(fi_data* head)
{
    fi_data* p = head;
    if(p == NULL)
    {
        return p;
    }
    while((NULL != p) && (NULL != p->next))
    {
        p = p->next;
    }
    return p;
}

5.取参数p的前一个节点

函数名称:get_previous

函数功能:取得数据节点p的前驱节点。具体实现如下:

//取得当前节点的前驱节点
fi_data* get_previous(fi_data* p)
{
    fi_data* previous = head;
    while(previous != NULL)
    {
        if(previous->next == p)
        {
            break;
        }
        previous = previous->next;
    }
 
    return previous;
}

6.输入收支的数据信息

函数名称:input_info

函数功能:提示用户按指定格式输入收支信息。具体实现如下:

void input_info(fi_info* info)
{
    printf(">请输入年月(YYYY/M):");
    //每次scanf()之前,都必须加入fflush(stdin)来清空当前缓冲区的内容。
    //如不清除,会带来意外的结果,如现在提示输入年月,比如不小心输入了一串字符
    //此时回车之后,缓冲区中就有一串字符,那么函数则一直检测为输入有误,
    //然后一直循环显示提示信息。
    fflush(stdin);
    scanf("%d/%d",&(info->year),&(info->month)); //* %d%d->%d/%d
    printf(">请输入家庭成员姓名(最大长度为%d):",MAX_NAME-1);
    fflush(stdin);
    scanf("%s",info->name);
    printf(">请输入金额:");
    fflush(stdin);
    scanf("%f",&(info->money));
 
    printf(">请输入备注(最大长度为%d):",MAX_DETAIL-1);
    fflush(stdin);
    //%[^
] 实现输入带空格的字符串, 其实它就是碰到回车键就结束输入
    scanf("%[^
]",info->detail);
}

7.显示收入或支出的数据

函数名称:show_info

函数功能:按指定格式显示收支信息。具体实现如下:

void show_info(fi_data* p[], int count)
{
    int i = 0;
    printf("+---+---------+------+----------+----------+------------------------+
");
    printf("|No.|  年-月  | 类型 |   姓 名  |   金 额  |     备注               |
");
    printf("+---+---------+------+----------+----------+------------------------+
");
    for(i = 0; i< count;i++)
    {
        printf("|%2d | %4d-%02d | %4s |   %-6s | %8.2f |    %-20s|
",
            i+1,
            p[i]->info.year,p[i]->info.month,
            p[i]->info.type == income ? "收入" : "支出",
            p[i]->info.name,
            p[i]->info.money,
            p[i]->info.detail);
        printf("+---+---------+------+----------+----------+------------------------+
");
    }
}
至此系统算是实现完毕了。

3.系统操作过程

1.F5运行,首先进入主菜单界面,充许用户输入0-9之间的数值,以实现不同的操作,如图:

====================================================

| 家庭财务管理系统                            |

+--------------------------------------------------+

| 收入管理                                    |

|      <1> 添加收入                           |

|      <2> 查询收入明细                       |

|      <3> 删除收入                           |

|      <4> 修改收入                           |

| 支出管理                                    |

|      <5> 添加支出                           |

|      <6> 查询支出明细                       |

|      <7> 删除支出                           |

|      <8> 修改支出                           |

| 统计                                        |

|      <9> 统计总收入总支出                   |

+--------------------------------------------------+

| 输入 <0> 退出系统                           |

+--------------------------------------------------+

>请选择要进行的操作[0-9]:

 

2.添加收入信息

选择操作1,进入添加收入信息操作,可以根据提示输入与收入相关的信息,如年月,家庭成员姓名等。输入完之后返回主菜单界面。如图:

====================================================

| 家庭财务管理系统                            |

+--------------------------------------------------+

| 收入管理                                    |

|      <1> 添加收入                           |

|      <2> 查询收入明细                       |

|      <3> 删除收入                           |

|      <4> 修改收入                           |

| 支出管理                                    |

|      <5> 添加支出                           |

|      <6> 查询支出明细                       |

|      <7> 删除支出                           |

|      <8> 修改支出                           |

| 统计                                        |

|      <9> 统计总收入总支出                   |

+--------------------------------------------------+

| 输入 <0> 退出系统                           |

+--------------------------------------------------+

>请选择要进行的操作[0-9]:1

>请输入年月(YYYY/M):2010/1

>请输入家庭成员姓名(最大长度为10):Mike

>请输入金额:100000

>请输入备注(最大长度为20):pay for working

 

3.查询收入明细

选择操作2,可以查询某年月的收入明细情况。首先系统提示输入要查询的年月,然后进行查询如果查询成功则显示查询到的信息,如果查询到的信息超过9条,则提示用户可以空格键翻页。如图:

====================================================

| 家庭财务管理系统                            |

+--------------------------------------------------+

| 收入管理                                    |

|      <1> 添加收入                           |

|      <2> 查询收入明细                       |

|      <3> 删除收入                           |

|      <4> 修改收入                           |

| 支出管理                                    |

|      <5> 添加支出                           |

|      <6> 查询支出明细                       |

|      <7> 删除支出                           |

|      <8> 修改支出                           |

| 统计                                        |

|      <9> 统计总收入总支出                   |

+--------------------------------------------------+

| 输入 <0> 退出系统                           |

+--------------------------------------------------+

>请选择要进行的操作[0-9]:2

>请输入要查询的年月(例如:2009/1)2010/1

+---+---------+------+----------+----------+------------------------+

|No.|  -  | 类型 |   姓 名  |   金 额  |     备注               |

+---+---------+------+----------+----------+------------------------+

| 1 | 2010-01 | 收入 |   Mike   | 10000.00 |    pay for working     |

+---+---------+------+----------+----------+------------------------+

| 2 | 2010-01 | 收入 |   Jhon   |   100.00 |    payment             |

+---+---------+------+----------+----------+------------------------+

| 3 | 2010-01 | 收入 |   Stive  |   203.00 |    working             |

+---+---------+------+----------+----------+------------------------+

| 4 | 2010-01 | 收入 |   Yse    |  3923.00 |    working for hours   |

+---+---------+------+----------+----------+------------------------+

| 5 | 2010-01 | 收入 |   Sasu   |   395.00 |    working for hours   |

+---+---------+------+----------+----------+------------------------+

| 6 | 2010-01 | 收入 |   Jane   |  8958.00 |    payment             |

+---+---------+------+----------+----------+------------------------+

| 7 | 2010-01 | 收入 |   Xlie   |   485.00 |    loan from bank      |

+---+---------+------+----------+----------+------------------------+

| 8 | 2010-01 | 收入 |   Xeson  |   495.00 |    working             |

+---+---------+------+----------+----------+------------------------+

>继续查找其它数据?(Y or N

  

查询信息超过9条时的情况(图示情况是已经按下空格键将余下的信息也一起显示出来了):

====================================================

| 家庭财务管理系统                            |

+--------------------------------------------------+

| 收入管理                                    |

|      <1> 添加收入                           |

|      <2> 查询收入明细                       |

|      <3> 删除收入                           |

|      <4> 修改收入                           |

| 支出管理                                    |

|      <5> 添加支出                           |

|      <6> 查询支出明细                       |

|      <7> 删除支出                           |

|      <8> 修改支出                           |

| 统计                                        |

|      <9> 统计总收入总支出                   |

+--------------------------------------------------+

| 输入 <0> 退出系统                           |

+--------------------------------------------------+

>请选择要进行的操作[0-9]:2

>请输入要查询的年月(例如:2009/1)2010/1

+---+---------+------+----------+----------+------------------------+

|No.|  -  | 类型 |   姓 名  |   金 额  |     备注               |

+---+---------+------+----------+----------+------------------------+

| 1 | 2010-01 | 收入 |   Mike   | 10000.00 |    pay for working     |

+---+---------+------+----------+----------+------------------------+

| 2 | 2010-01 | 收入 |   Jhon   |   100.00 |    payment             |

+---+---------+------+----------+----------+------------------------+

| 3 | 2010-01 | 收入 |   Stive  |   203.00 |    working             |

+---+---------+------+----------+----------+------------------------+

| 4 | 2010-01 | 收入 |   Yse    |  3923.00 |    working for hours   |

+---+---------+------+----------+----------+------------------------+

| 5 | 2010-01 | 收入 |   Sasu   |   395.00 |    working for hours   |

+---+---------+------+----------+----------+------------------------+

| 6 | 2010-01 | 收入 |   Jane   |  8958.00 |    payment             |

+---+---------+------+----------+----------+------------------------+

| 7 | 2010-01 | 收入 |   Xlie   |   485.00 |    loan from bank      |

+---+---------+------+----------+----------+------------------------+

| 8 | 2010-01 | 收入 |   Xeson  |   495.00 |    working             |

+---+---------+------+----------+----------+------------------------+

| 9 | 2010-01 | 收入 |   Weon   |   987.00 |    for test more than 9|

+---+---------+------+----------+----------+------------------------+

>输入空格并回车翻页,按其它键退出....

+---+---------+------+----------+----------+------------------------+

|No.|  -  | 类型 |   姓 名  |   金 额  |     备注               |

+---+---------+------+----------+----------+------------------------+

| 1 | 2010-01 | 收入 |   Mik    |   618.00 |    for testing now     |

+---+---------+------+----------+----------+------------------------+

| 2 | 2010-01 | 收入 |   Jhons  |   781.00 |    for testing 9       |

+---+---------+------+----------+----------+------------------------+

>继续查找其它数据?(Y or N

 

4.删除收入信息

选择操作3,则进行删除操作,系统提示输入要查询的年月,然后进行查询,如果查询成功,系统会将符号查询条件的信息显示出来,然后提示用户输入要删除的信息的序号,完成删除操作。如果查询到,则给出提示信息。删除完成之后将删除完之后的信息重新打印出来,并可继续删除操作或返回主界面。如图:

====================================================

| 家庭财务管理系统                            |

+--------------------------------------------------+

| 收入管理                                    |

|      <1> 添加收入                           |

|      <2> 查询收入明细                       |

|      <3> 删除收入                           |

|      <4> 修改收入                           |

| 支出管理                                    |

|      <5> 添加支出                           |

|      <6> 查询支出明细                       |

|      <7> 删除支出                           |

|      <8> 修改支出                           |

| 统计                                        |

|      <9> 统计总收入总支出                   |

+--------------------------------------------------+

| 输入 <0> 退出系统                           |

+--------------------------------------------------+

>请选择要进行的操作[0-9]:3

>请输入要查询的年月(例如:2009/1):2010/1

+---+---------+------+----------+----------+------------------------+

|No.|  -  | 类型 |   姓 名  |   金 额  |     备注               |

+---+---------+------+----------+----------+------------------------+

| 1 | 2010-01 | 收入 |   Mike   | 10000.00 |    pay for working     |

+---+---------+------+----------+----------+------------------------+

| 2 | 2010-01 | 收入 |   Jhon   |   100.00 |    payment             |

+---+---------+------+----------+----------+------------------------+

| 3 | 2010-01 | 收入 |   Stive  |   203.00 |    working             |

+---+---------+------+----------+----------+------------------------+

| 4 | 2010-01 | 收入 |   Yse    |  3923.00 |    working for hours   |

+---+---------+------+----------+----------+------------------------+

| 5 | 2010-01 | 收入 |   Sasu   |   395.00 |    working for hours   |

+---+---------+------+----------+----------+------------------------+

| 6 | 2010-01 | 收入 |   Jane   |  8958.00 |    payment             |

+---+---------+------+----------+----------+------------------------+

| 7 | 2010-01 | 收入 |   Xlie   |   485.00 |    loan from bank      |

+---+---------+------+----------+----------+------------------------+

| 8 | 2010-01 | 收入 |   Xeson  |   495.00 |    working             |

+---+---------+------+----------+----------+------------------------+

| 9 | 2010-01 | 收入 |   Weon   |   987.00 |    for test more than 9|

+---+---------+------+----------+----------+------------------------+

>输入空格并回车翻页.输入对应的序号删除.其它键退出.请输入:9

>删除成功!

+---+---------+------+----------+----------+------------------------+

|No.|  -  | 类型 |   姓 名  |   金 额  |     备注               |

+---+---------+------+----------+----------+------------------------+

| 1 | 2010-01 | 收入 |   Mike   | 10000.00 |    pay for working     |

+---+---------+------+----------+----------+------------------------+

| 2 | 2010-01 | 收入 |   Jhon   |   100.00 |    payment             |

+---+---------+------+----------+----------+------------------------+

| 3 | 2010-01 | 收入 |   Stive  |   203.00 |    working             |

+---+---------+------+----------+----------+------------------------+

| 4 | 2010-01 | 收入 |   Yse    |  3923.00 |    working for hours   |

+---+---------+------+----------+----------+------------------------+

| 5 | 2010-01 | 收入 |   Sasu   |   395.00 |    working for hours   |

+---+---------+------+----------+----------+------------------------+

| 6 | 2010-01 | 收入 |   Jane   |  8958.00 |    payment             |

+---+---------+------+----------+----------+------------------------+

| 7 | 2010-01 | 收入 |   Xlie   |   485.00 |    loan from bank      |

+---+---------+------+----------+----------+------------------------+

| 8 | 2010-01 | 收入 |   Xeson  |   495.00 |    working             |

+---+---------+------+----------+----------+------------------------+

| 9 | 2010-01 | 收入 |   Mik    |   618.00 |    for testing now     |

+---+---------+------+----------+----------+------------------------+

>输入空格并回车翻页.输入对应的序号删除.其它键退出.请输入:

  

5.修改收入信息

选择操作4,进行修改收入信息的操作,系统提示输入要查询的年月,如果查询成功,系统会将符合条件的信息显示出来,再提示用户输入要修改的收入信息的序号,然后调用input_info函数使按照提示信息进行修改操作。修改完成后,系统会立即打印修改后的信息,并提示是否继续操作。如图:

====================================================

| 家庭财务管理系统                            |

+--------------------------------------------------+

| 收入管理                                    |

|      <1> 添加收入                           |

|      <2> 查询收入明细                       |

|      <3> 删除收入                           |

|      <4> 修改收入                           |

| 支出管理                                    |

|      <5> 添加支出                           |

|      <6> 查询支出明细                       |

|      <7> 删除支出                           |

|      <8> 修改支出                           |

| 统计                                        |

|      <9> 统计总收入总支出                   |

+--------------------------------------------------+

| 输入 <0> 退出系统                           |

+--------------------------------------------------+

>请选择要进行的操作[0-9]:4

>请输入要查询的年月(例如:2009/1):2010/2

+---+---------+------+----------+----------+------------------------+

|No.|  -  | 类型 |   姓 名  |   金 额  |     备注               |

+---+---------+------+----------+----------+------------------------+

| 1 | 2010-02 | 收入 |   Alice  |  2003.00 |    working             |

+---+---------+------+----------+----------+------------------------+

>输入对应的序号修改,其它键退出,请输入:1

>请输入年月(YYYY/M):2010/2

>请输入家庭成员姓名(最大长度为10):Mikes

>请输入金额:9589

>请输入备注(最大长度为20):payment

+---+---------+------+----------+----------+------------------------+

|No.|  -  | 类型 |   姓 名  |   金 额  |     备注               |

+---+---------+------+----------+----------+------------------------+

| 1 | 2010-02 | 收入 |   Mikes  |  9589.00 |    payment             |

+---+---------+------+----------+----------+------------------------+

>修改成功...

>继续查找其它数据?(Y or N )

 

支出管理操作与收入管理操作类似,下面就不再赘述操作过程,而直接上图了

6.添加支出信息,如图:

====================================================

| 家庭财务管理系统                            |

+--------------------------------------------------+

| 收入管理                                    |

|      <1> 添加收入                           |

|      <2> 查询收入明细                       |

|      <3> 删除收入                           |

|      <4> 修改收入                           |

| 支出管理                                    |

|      <5> 添加支出                           |

|      <6> 查询支出明细                       |

|      <7> 删除支出                           |

|      <8> 修改支出                           |

| 统计                                        |

|      <9> 统计总收入总支出                   |

+--------------------------------------------------+

| 输入 <0> 退出系统                           |

+--------------------------------------------------+

>请选择要进行的操作[0-9]:5

>请输入年月(YYYY/M):2010/3

>请输入家庭成员姓名(最大长度为10):Woen

>请输入金额:85

>请输入备注(最大长度为20):for breakfirst

 

7.查询支出明细,如图:

====================================================

| 家庭财务管理系统                            |

+--------------------------------------------------+

| 收入管理                                    |

|      <1> 添加收入                           |

|      <2> 查询收入明细                       |

|      <3> 删除收入                           |

|      <4> 修改收入                           |

| 支出管理                                    |

|      <5> 添加支出                           |

|      <6> 查询支出明细                       |

|      <7> 删除支出                           |

|      <8> 修改支出                           |

| 统计                                        |

|      <9> 统计总收入总支出                   |

+--------------------------------------------------+

| 输入 <0> 退出系统                           |

+--------------------------------------------------+

>请选择要进行的操作[0-9]:6

>请输入要查询的年月(例如:2009/1)2010/3

+---+---------+------+----------+----------+------------------------+

|No.|  -  | 类型 |   姓 名  |   金 额  |     备注               |

+---+---------+------+----------+----------+------------------------+

| 1 | 2010-03 | 支出 |   Stensn |   623.00 |    watch moving        |

+---+---------+------+----------+----------+------------------------+

| 2 | 2010-03 | 支出 |   Woen   |    85.00 |    for breakfirst      |

+---+---------+------+----------+----------+------------------------+

>继续查找其它数据?(Y or N

 

8.删除支出信息,如图:

====================================================

| 家庭财务管理系统                            |

+--------------------------------------------------+

| 收入管理                                    |

|      <1> 添加收入                           |

|      <2> 查询收入明细                       |

|      <3> 删除收入                           |

|      <4> 修改收入                           |

| 支出管理                                    |

|      <5> 添加支出                           |

|      <6> 查询支出明细                       |

|      <7> 删除支出                           |

|      <8> 修改支出                           |

| 统计                                        |

|      <9> 统计总收入总支出                   |

+--------------------------------------------------+

| 输入 <0> 退出系统                           |

+--------------------------------------------------+

>请选择要进行的操作[0-9]:7

>请输入要查询的年月(例如:2009/1):2010/3

+---+---------+------+----------+----------+------------------------+

|No.|  -  | 类型 |   姓 名  |   金 额  |     备注               |

+---+---------+------+----------+----------+------------------------+

| 1 | 2010-03 | 支出 |   Stensn |   623.00 |    watch moving        |

+---+---------+------+----------+----------+------------------------+

| 2 | 2010-03 | 支出 |   Woen   |    85.00 |    for breakfirst      |

+---+---------+------+----------+----------+------------------------+

>输入对应的序号删除.其它键退出.请输入:1

>删除成功...

>继续查找其它数据?(Y or N)N

 

9.修改支出信息,如图:

====================================================

| 家庭财务管理系统                            |

+--------------------------------------------------+

| 收入管理                                    |

|      <1> 添加收入                           |

|      <2> 查询收入明细                       |

|      <3> 删除收入                           |

|      <4> 修改收入                           |

| 支出管理                                    |

|      <5> 添加支出                           |

|      <6> 查询支出明细                       |

|      <7> 删除支出                           |

|      <8> 修改支出                           |

| 统计                                        |

|      <9> 统计总收入总支出                   |

+--------------------------------------------------+

| 输入 <0> 退出系统                           |

+--------------------------------------------------+

>请选择要进行的操作[0-9]:8

>请输入要查询的年月(例如:2009/1):2010/3

+---+---------+------+----------+----------+------------------------+

|No.|  -  | 类型 |   姓 名  |   金 额  |     备注               |

+---+---------+------+----------+----------+------------------------+

| 1 | 2010-03 | 支出 |   Woen   |    85.00 |    for breakfirst      |

+---+---------+------+----------+----------+------------------------+

>输入对应的序号修改,其它键退出,请输入:1

>请输入年月(YYYY/M):2010/3

>请输入家庭成员姓名(最大长度为10):Stenph

>请输入金额:2039

>请输入备注(最大长度为20):lunch fee

+---+---------+------+----------+----------+------------------------+

|No.|  -  | 类型 |   姓 名  |   金 额  |     备注               |

+---+---------+------+----------+----------+------------------------+

| 1 | 2010-03 | 支出 |   Stenph |  2039.00 |    lunch fee           |

+---+---------+------+----------+----------+------------------------+

>修改成功...

>继续查找其它数据?(Y or N )

 

10.统计收支结余,如图:

====================================================

| 家庭财务管理系统                            |

+--------------------------------------------------+

| 收入管理                                    |

|      <1> 添加收入                           |

|      <2> 查询收入明细                       |

|      <3> 删除收入                           |

|      <4> 修改收入                           |

| 支出管理                                    |

|      <5> 添加支出                           |

|      <6> 查询支出明细                       |

|      <7> 删除支出                           |

|      <8> 修改支出                           |

| 统计                                        |

|      <9> 统计总收入总支出                   |

+--------------------------------------------------+

| 输入 <0> 退出系统                           |

+--------------------------------------------------+

>请选择要进行的操作[0-9]:9

 

+------------+------------+------------+

|  合计收入  |  合计支出  |    结余    |

|   125647.00|     2039.00|   123608.00|

+------------+------------+------------+

>按任意键返回主菜单...

 

4.总结与Bug

Bug.1

1>FinanceMain.obj : error LNK2019: unresolved external symbol "struct _fi_data * __cdecl get_last(struct _fi_data *)" (?get_last@@YAPAU_fi_data@@PAU1@@Z) referenced in function "void __cdecl add_income(void)" (?add_income@@YAXXZ)
原因:

在函数实现的时候把get_last()函数写错了,本应该写成:fi_data* get_last(fi_data* head)

但却写成了fi_data* get_last() 导致在 add_income()中调用的时候找不到这个函数,虽然声明中是声明为:fi_data* get_last(fi_data* head) , 但是实现的时候却不是这个函数,所以它被调用的时候 由于实现的是 get_last() 而不是get_last(fi_data* head) 所以导致 用get_last(head)->next 时, 找不到返回的fi_data* 指针,所以无法进行->next , 所以找不到能返回为fi_data* 指针的函数,即报链接错误。

Bug.2

delete_data()函数,

//修改 input- 48 为 input - 48 -1 因为数组的下标是从0开始的
                    i = input - 48 -1;

否则输入最后一个序号, 则最后删除的时候因为数组越界,造成错误,因为数组小标是从0开始排起的

原文地址:https://www.cnblogs.com/AI-Algorithms/p/3391430.html