C語言代碼實現通訊錄管理系統

本文實例為大傢分享瞭C語言實現通訊錄管理系統,供大傢參考,具體內容如下

一、需求分析

運用C語言實現一個簡單的通訊錄管理系統,要求對數據有 增刪改查清排顯 等功能的實現(這裡由於還沒學到文件,所以下面所有的存儲都是在內存中,也就是當程序結束的時候添加的信息都會清空掉 – 後續會加以改進的)

二、程序結構

在基本的程序設計中,都會將項目分為三個文件:

1、test.c – 主函數(負責調用各個功能的接口)
2、contact.c – 實現各個模塊的功能
3、contact.h – 用於存放頭文件、宏定義、函數聲明等

三、頭文件內容的介紹

#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <stdlib.h>

#define MAX 1000
#define NAME_MAX 20
#define SEX_MAX 5
#define ADDR_MAX 50
#define TELE_MAX 12

#define DEFAULT_SZ 3

// 數組版本
//typedef struct PeoInfo
//{
//    char name[NAME_MAX];
//    int age;
//    char sex[SEX_MAX];
//    char addr[ADDR_MAX];
//    char tele[TELE_MAX];
//}PeoInfo;
//
//typedef struct Contact
//{
//    //PeoInfo data[MAX];  // 存放信息
//    PeoInfo data[MAX];
//    int sz; // 存放通訊錄有幾個人的信息
//}Contact;

// 每個人的基本信息
typedef struct PeoInfo
{
    char name[NAME_MAX];
    int age;
    char sex[SEX_MAX];
    char addr[ADDR_MAX];
    char tele[TELE_MAX];
}PeoInfo;

typedef struct Contact
{
    //PeoInfo data[MAX];  // 存放信息
    PeoInfo* data;
    int sz; // 存放通訊錄有幾個人的信息
    int capacity; // 記錄當前通訊錄的最大容量
}Contact;


void DestoryContact(Contact* pc);
void InitContact(Contact* pc);
void AddContact(Contact* pc);
void ShowContact(const Contact* pc);
void DeleteContact(Contact* pc);
void SearchContact(const Contact* pc);
void ModifyContact(Contact* pc);
void SortContact(Contact* pc);

裡面被註釋的代碼則是之前的數組版本,規定死瞭通訊錄大小;而下面我們會用動態增容來進行實現。頭文件裡面用宏定義主要為瞭之後我們更改對應的大小的時候更加的方便;結構體主要存放的就是三大部分:

1. 一個指向每個人基本信息的結構體指針;2. sz記錄著通訊錄的記錄條數;3. capacity 存放通訊錄的容量

四、模塊化實現各個功能

(1)主函數實現

int main()
{
    int input = 0;
    Contact con; // 創建通訊錄

    InitContact(&con);

    do
    {
        menu();
        printf("請選擇:->");
        scanf("%d", &input);

        switch (input)
        {
        case ADD:
            AddContact(&con);
            break;
        case DEL:
            DeleteContact(&con);
            break;
        case SEARCH:
            SearchContact(&con);
            break;
        case MODIFY:
            ModifyContact(&con);
            break;
        case SORT:
            SortContact(&con);
            break;
        case SHOW:
            ShowContact(&con);
            break;
        case EXIT:
            DestoryContact(&con);
            printf("退出\n");
            break;
        default:
            printf("輸入錯誤,請重新輸入\n");
            break;
        }
    } while (input);

    return 0;
}

一般函數的主函數裡面都可以用do ……while結構來控制程序的執行順序,在case後面用的則是 枚舉常量 ,這樣能做到更好的見名知意。

(2)初始化通訊錄

void InitContact(Contact* pc)
{
    assert(pc);
    pc->sz = 0;
    PeoInfo* tmp = (PeoInfo*)malloc(DEFAULT_SZ * sizeof(PeoInfo));
    if (tmp != NULL)
    {
        pc->data = tmp;
    }
    else
    {
        printf("InitContact()::%s\n", strerror(errno));
    }

    pc->capacity = DEFAULT_SZ;
}

這裡設定瞭通訊錄的起始大小,後面可以通過動態增容來擴大(而數組版本的話就沒有這麼靈活瞭)

(3)添加聯系人信息

void check_capacity(Contact* pc)
{
    assert(pc);
    if (pc->sz == pc->capacity)
    {
        // 增容
        PeoInfo* tmp = (PeoInfo*)realloc(pc->data, (pc->capacity + 2)*sizeof(PeoInfo));
        if (tmp != NULL)
        {
            pc->data = tmp;
            pc->capacity += 2;
            printf("增容成功\n");
        }
        else
        {
            printf("InitContact()::%s\n", strerror(errno));
        }
    }
}

void AddContact(Contact* pc)
{
    assert(pc);
    check_capacity(pc);

    // 添加內容
    printf("名字:");
    scanf("%s", pc->data[pc->sz].name);
    printf("年齡:");
    scanf("%d", &(pc->data[pc->sz].age));
    printf("性別:");
    scanf("%s", pc->data[pc->sz].sex);
    printf("地址:");
    scanf("%s", pc->data[pc->sz].addr);
    printf("電話:");
    scanf("%s", pc->data[pc->sz].tele);
    
    pc->sz++;
    printf("添加成功\n");
}

這裡實現瞭一個check_capacity 的接口,用來判斷當前通訊錄是否存滿瞭;若滿瞭則進行擴容(一般按2倍進行擴容),註意:添加成功後需要對 sz++

(4)刪除聯系人信息

int FindByName(const Contact* pc, char* name)
{
    int i = 0;
    for (i = 0; i < pc->sz; i++)
    {
        if (strcmp(pc->data[i].name, name) == 0)
        {
            return i;
        }
    }
    return -1;
}

void DeleteContact(Contact* pc)
{
    char name[NAME_MAX] = { 0 };
    assert(pc);

    if (pc->sz == 0)
    {
        printf("通訊錄為空\n");
        return;
    }

    // 根據輸入的人名進行刪除
    printf("請輸入要刪除的人名:");
    scanf("%s", name);

    int pos = FindByName(pc, name);

    if (pos == -1)
    {
        printf("要刪除的人不存在\n");
    }
    else
    {
        // 刪除
        //int j = 0;
        //for (j = pos; j < pc->sz-1; j++)  // sz-1為瞭防止j+1非法訪問
        //{
        //    pc->data[j] = pc->data[j + 1];
        //}

        //pc->sz--;
        //printf("刪除成功\n");
        //if (pc->sz>1)
        memmove((pc->data) + pos, (pc->data) + pos + 1, (pc->sz - pos)*sizeof(PeoInfo));
        pc->sz--;
    }
}

這裡我就隻做瞭按照名字進行刪除,你們也可以都嘗試一下喲。下面寫瞭兩種刪除方法(這裡的按名字刪除前需要進行查找,因此我們也可以直接使用我們下面要實現的查找函數,但如果查找裡面進行瞭其他功能,那時不能直接使用的喲)

(5)查找聯系人信息

void SearchContact(const Contact* pc)

{
    char name[NAME_MAX] = { 0 };
    assert(pc);

    if (pc->sz == 0)
    {
        printf("通訊錄為空\n");
        return;
    }

    printf("請輸入要查找的姓名:");
    scanf("%s", name);
    int i = FindByName(pc, name);

    if(-1 == i)
    {
        // 沒有找到
        printf("你要查找的人不存在\n");
    }
    else
    {
        // 找到瞭
        printf("%-10s\t%-5s\t%-5s\t%-20s\t%-13s\n", 
                "姓名", "年齡", "性別", "地址", "電話");
        printf("%-10s\t%-5d\t%-5s\t%-20s\t%-13s\n",
            pc->data[i].name, pc->data[i].age, pc->data[i].sex, pc->data[i].addr, pc->data[i].tele);
    }
}

這裡如果查找到瞭後,我打印顯示瞭一下

(6)更改聯系人信息

void ModifyContact(Contact* pc)
{
    char name[NAME_MAX] = { 0 };
    assert(pc);

    if (pc->sz == 0)
    {
        printf("通訊錄為空\n");
        return;
    }

    printf("請輸入要修改信息的姓名:");
    scanf("%s", name);

    int i = FindByName(pc, name);

    // 修改內容
    printf("新的名字:");
    scanf("%s", pc->data[i].name);
    printf("新的年齡:");
    scanf("%d", &(pc->data[i].age));
    printf("新的性別:");
    scanf("%s", pc->data[i].sex);
    printf("新的地址:");
    scanf("%s", pc->data[i].addr);
    printf("新的電話:");
    scanf("%s", pc->data[i].tele);

    printf("修改後的信息為:\n");

    printf("%-10s\t%-5s\t%-5s\t%-20s\t%-13s\n", "姓名", "年齡", "性別", "地址", "電話");
    printf("%-10s\t%-5d\t%-5s\t%-20s\t%-13s\n",
        pc->data[i].name, pc->data[i].age, pc->data[i].sex, pc->data[i].addr, pc->data[i].tele);
}

(7)顯示所有聯系人信息

void ShowContact(const Contact* pc)
{
    assert(pc);
    // 打印標題
    printf("%-10s\t%-5s\t%-5s\t%-20s\t%-13s\n", "姓名", "年齡", "性別", "地址", "電話");
    int i = 0;
    for (i = 0; i < pc->sz; i++)
    {
        printf("%-10s\t%-5d\t%-5s\t%-20s\t%-13s\n",
            pc->data[i].name, pc->data[i].age, pc->data[i].sex, pc->data[i].addr, pc->data[i].tele);
    }
}

直接遍歷打印即可

(8)對聯系人信息進行排序

int cmp_by_name(const void* e1, const void* e2)
{
    return strcmp(((PeoInfo*)e1)->name, ((PeoInfo*)e2)->name);
}

int cmp_by_age(const void* e1, const void* e2)
{
    return ((PeoInfo*)e1)->age - ((PeoInfo*)e2)->age;
    // return strcmp(, ((PeoInfo*)e2)->name);
}

void SortContact(Contact* pc)
{
    int choice = 0;
    printf("請選擇:(1、按姓名;2、按年齡)");
    scanf("%d", &choice);

    if (1 == choice)
    {
        qsort(pc->data, pc->sz, sizeof(pc->data[0]), cmp_by_name);

        printf("排序後的數據:\n");
        ShowContact(pc);
    }
    else if (2 == choice)
    {
        qsort(pc->data, pc->sz, sizeof(pc->data[0]), cmp_by_age);
        printf("排序後的數據:\n");
        ShowContact(pc);
    }
    else
    {
        printf("輸入有誤\n");
    }
}

這裡使用qsort實現的排序,可以對 姓名 或 年齡進行排序的

(9)退出時銷毀通訊錄

void DestoryContact(Contact* pc)
{
    assert(pc);
    free(pc->data);
    pc->data = NULL;
}

直接將裡面指向有效內容的 pc->data 進行free釋放就行,因為它本來就是動態開辟的,後面要有個好習慣,free之後將其置為NULL,不然就為野指針瞭。

總結:

以上就是動態增容版本通訊錄的全部代碼,盡管用瞭動態增容,裡面仍然會存在很多的空間浪費,後面等博主學瞭鏈表後,在進行改進。

希望這篇文章對大傢的學習有所幫助,也希望大傢多多支持WalkonNet。

推薦閱讀: