结构体及它的应用——通讯录

修改结构体的默认对齐数

在VS这个环境下,结构体的默认对齐数是8,有时这个默认对齐数并不适用,这是我们可以对它进行修改。

#include<stdio.h>
#pragma pack(4)//将默认对齐数改成4
struck s1
{
    char c1;
    int i;
    char c2;
};
#pragma pack()//将默认对齐数还原成环境默认的8
int main()
{
    printf("%d",sizeof(s1));//得到的结果是6
    return 0;
}

为什么得到6,详见我的上一条博客http://t.csdn.cn/UJShO

默认对齐数的修改让我们对结构体的使用更加灵活。


结构体传参

struct S
{
    int data[1000];
    int num;
};
struct S s={{1,2,3,4},1000};
//结构体传参
void print1(struct S s)
{
    printf("%d\n",s.num);
}
//结构体地址传参
void print2(struct S* ps)
{
    printf("%d\n",ps->num);
}

int main()
{
    print1(s);//传结构体
    print2(&s);//传地址
    return 0;
}

上面的print1和print2函数哪个更好些?

在运用的过程中我们首选为print2函数,因为函数传参时,参数是需要压栈的,会有时间和空间上的系统开销。如果传递一个结构体对象的时候,结构体过大,参数压栈的系统开销比较大,会导致性能的下降


结构体的应用——通讯录

这个通讯录需要包括:

  1. 人的信息:姓名、年龄、性别、地址、电话

  1. 可以放100个人的信息

  1. 功能:增加联系人、删除指定的联系人、查找指定联系人的信息、修改指定联系人的信息、显示所有联系人的信息

在编译器里我们需要创建一个头文件contact.h和两个源文件test.c、contact.c

contact.h负责函数声明

contact.c负责通讯录的实现

test.c负责通讯录的测试

要想实现这个通讯录的编写,大体可以分为以下三步:


第一步:菜单
//test.c
#define _CRT_SECURE_NO_WARNINGS 1

#include<stdio.h>

void menu()
{
    printf("**************************************\n");
    printf("******    1.add       2.del     ******\n");
    printf("******    3.search    4.modify  ******\n");
    printf("******    5.show                ******\n");
    printf("******    0.exit                ******\n");
    printf("**************************************\n");
}

int main()
{
    int input = 0;
    do
    {
        menu();//将菜单打印在屏幕上
        printf("请选择:>");
        scanf("%d", &input);//通过输入数字来选择接下来的操作
        switch (input)
        {
        case 1:
            break;
        case 2:
            break;
        case 3:
            break;
        case 4:
            break;
        case 5:
            break;
        case 0:
            printf("退出通讯录\n");
            break;
        default:
            printf("选择错误\n");
            break;
        }
    } while (input);//当我们输入0时跳出该循环
    return 0;
}

这很常规,要想使用这个通讯录,我们首先需要一个菜单,通过输入值来选择接下来要进行的操作。


第二步:创建结构体并对其进行初始化

我们将人的信息(姓名、年龄、性别、住址、手机号)放在结构体PeoInfo中,设想我们在进行增加联系人的操作时,若原本就有两个联系人,就需要从第三个位置开始存放,因此我们需要一个变量来记载此刻已经存放的联系人数量,所以有了变量sz。

为了让初始化内容更灵活,我们选择将其封装成函数InitContact,通过对这个函数的修改,我们可以对其初始化内容进行更灵活的选择。值得注意的是,这里用了地址传参。

//contact.h

#define MAX 100
#define NAME_MAX 20
#define SEX_MAX 5
#define ADDR_MAX 30
#define TELE_MAX 12
//人的信息
typedef struct PeoInfo
{
    char name[NAME_MAX];//姓名
    int age;//年龄
    char sex[SEX_MAX];//性别
    char addr[ADDR_MAX];//住址
    char tele[TELE_MAX];//手机号
}PeoInfo;//重命名为PeoInfo

typedef struct Contact
{
    PeoInfo data[100];//存放人的信息的
    int sz;//当前已存放的信息个数
}Contact;

void InitContact(Contact* pc);//声明
//test.c
//在int input = 0的后面一行
    Contact con;
//初始化通讯录
    InitContact(&con);//结构体地址传参
//contact.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "contact.h"

void InitContact(Contact* pc)
{
    pc->sz = 0;
    memset(pc->data, 0, sizeof(pc->data));//用memset对它进行初始化
}

第三步:完善通讯录的各个功能

以下代码均写在contact.c下

增加联系人
void AddContact(Contact* pc)
{
    if (pc->sz == 100)
    {
        printf("通讯录已满,无法添加\n");
        return;
    }
    //没有满的时候,增加一个人的信息
    printf("请输入名字:>");
    scanf("%s", pc->data[pc->sz].name);
    printf("请输入年龄:>");
    scanf("%d", &(pc->data[pc->sz].age));
    printf("请输入性别:>");
    scanf("%s", pc->data[pc->sz].sex);
    printf("请输入地址:>");
    scanf("%s", pc->data[pc->sz].addr);
    printf("请输入电话:>");
    scanf("%s", pc->data[pc->sz].tele);
    printf("添加成功\n");

    pc->sz++;
}

显示联系人
void ShowContact(Contact* pc)
{
    int i = 0;
    printf("%-20s\t%-4s\t%-5s\t%-20s\t%-10s\n", "名字", "年龄", "性别", "住址", "手机号");
    for (i = 0; i < pc->sz; i++)
    {
        printf("%-20s\t%-4d\t%-5s\t%-20s\t%-10s\n", pc->data[i].name,
                                                pc->data[i].age,
                                                pc->data[i].sex,
                                                pc->data[i].addr,
                                                pc->data[i].tele);
    }
} 

删除指定联系人
void DelContact(Contact* pc)
{
    char name[NAME_MAX] = { 0 };
    if (pc->sz == 0)
    {
        printf("通讯录为空,无法删除\n");
        return;
    }
    //找到要删除的人
    printf("请输入要删除的人的名字:>");
    scanf("%s", name);
    int i = 0;
    int del = 0;
    for (i = 0; i < pc->sz; i++)
    {
        if (strcmp(pc->data[i].name, name) == 0)
        {
            del = i;
            break;
        }
    }
    //删除
    for (i = del; i < pc->sz-1; i++)
    {
        pc->data[i] = pc->data[i + 1];//用后面一个联系人的全部信息覆盖前一个联系人的全部信息
        //以达到删除的目的
    }
    pc->sz--;
    printf("删除成功\n");
}

将“找到这个人”的一整套操作封装成一个函数,又可以把上面这段代码写成以下代码

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 DelContact(Contact* pc)
{
    char name[NAME_MAX] = { 0 };
    if (pc->sz == 0)
    {
        printf("通讯录为空,无法删除\n");
        return;
    }
    //找到要删除的人
    printf("请输入要删除的人的名字:>");
    scanf("%s", name);
    int ret = FindByName(pc, name);
    if (-1 == ret)
    {
        printf("要删除的人不存在\n");
        return;
    }
    int i = 0;
    //删除
    for (i = ret; i < pc->sz-1; i++)
    {
        pc->data[i] = pc->data[i + 1];//用后面一个联系人的全部信息覆盖前一个联系人的全部信息
        //以达到删除的目的
    }
    pc->sz--;
    printf("删除成功\n");
}

查找联系人
void SearchContact(const Contact* pc)
{
    char name[NAME_MAX];
    //找到这个人
    printf("请输入你想查找的人的名字:>");
    scanf("%s", name);
    int pos = FindByName(pc, name);
    if (-1 == pos)
    {
        printf("要查找的人不存在\n");
        return;
    }
    //若找到了,将这个人的信息打印出来
    printf("%-20s\t%-4s\t%-5s\t%-20s\t%-10s\n", "名字", "年龄", "性别", "住址", "手机号");
    printf("%-20s\t%-4d\t%-5s\t%-20s\t%-10s\n", pc->data[pos].name,
                                                pc->data[pos].age,
                                                pc->data[pos].sex,
                                                pc->data[pos].addr,
                                                pc->data[pos].tele);

}

修改联系人
void ModifyContact(Contact* pc)
{
    assert(pc);
    char name[NAME_MAX] = { 0 };
    printf("请输入你想修改的人的名字:>");
    scanf("%s", name);
    int pos = FindByName(pc, name);
    if (-1 == pos)
    {
        printf("要修改的人不存在\n");
        return;
    }
    printf("请输入名字:>");
    scanf("%s", pc->data[pos].name);
    printf("请输入年龄:>");
    scanf("%d", &(pc->data[pos].age));
    printf("请输入性别:>");
    scanf("%s", pc->data[pos].sex);
    printf("请输入地址:>");
    scanf("%s", pc->data[pos].addr);
    printf("请输入电话:>");
    scanf("%s", pc->data[pos].tele);
    printf("修改成功\n");
}


通讯录代码总览
//contact.h

#pragma once

#include <string.h>
#include<stdio.h>
#include<assert.h>

#define MAX 100
#define NAME_MAX 20
#define SEX_MAX 5
#define ADDR_MAX 30
#define TELE_MAX 12
//人的信息

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];//存放人的信息的
    int sz;//当前已存放的信息个数
}Contact;

//初始化通讯录
void InitContact(Contact* pc);//声明

//增加联系人
void AddContact(Contact* pc);

//删除指定联系人
void DelContact(Contact* pc);

//显示通讯录中的信息
void ShowContact(const Contact* pc);

//查找联系人并显示
void SearchContact(const Contact* pc);

//修改指定联系人的信息
void ModifyContact(Contact *pc);
//contact.c

#define _CRT_SECURE_NO_WARNINGS 1

#include "contact.h"

void InitContact(Contact* pc)
{
    assert(pc);
    pc->sz = 0;
    memset(pc->data, 0, sizeof(pc->data));
}

void AddContact(Contact* pc)
{
    assert(pc);
    if (pc->sz == MAX)
    {
        printf("通讯录已满,无法添加\n");
        return;
    }
    //没有满的时候,增加一个人的信息
    printf("请输入名字:>");
    scanf("%s", pc->data[pc->sz].name);
    printf("请输入年龄:>");
    scanf("%d", &(pc->data[pc->sz].age));
    printf("请输入性别:>");
    scanf("%s", pc->data[pc->sz].sex);
    printf("请输入地址:>");
    scanf("%s", pc->data[pc->sz].addr);
    printf("请输入电话:>");
    scanf("%s", pc->data[pc->sz].tele);
    printf("添加成功\n");

    pc->sz++;
}


void ShowContact(const Contact* pc)
{
    assert(pc);
    int i = 0;
    printf("%-20s\t%-4s\t%-5s\t%-20s\t%-10s\n", "名字", "年龄", "性别", "住址", "手机号");
    for (i = 0; i < pc->sz; i++)
    {
        printf("%-20s\t%-4d\t%-5s\t%-20s\t%-10s\n", pc->data[i].name,
                                                pc->data[i].age,
                                                pc->data[i].sex,
                                                pc->data[i].addr,
                                                pc->data[i].tele);
    }
}



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


void DelContact(Contact* pc)
{
    assert(pc);
    char name[NAME_MAX] = { 0 };
    if (pc->sz == 0)
    {
        printf("通讯录为空,无法删除\n");
        return;
    }
    //找到要删除的人
    printf("请输入要删除的人的名字:>");
    scanf("%s", name);
    int ret = FindByName(pc, name);
    if (-1 == ret)
    {
        printf("要删除的人不存在\n");
        return;
    }
    int i = 0;
    //删除
    for (i = ret; i < pc->sz-1; i++)
    {
        pc->data[i] = pc->data[i + 1];//用后面一个联系人的全部信息覆盖前一个联系人的全部信息
        //以达到删除的目的
    }
    pc->sz--;
    printf("删除成功\n");
}

void SearchContact(const Contact* pc)
{
    assert(pc);
    char name[NAME_MAX];
    printf("请输入你想查找的人的名字:>");
    scanf("%s", name);
    int pos = FindByName(pc, name);
    if (-1 == pos)
    {
        printf("要查找的人不存在\n");
        return;
    }
    //若找到了,将这个人的信息打印出来
    printf("%-20s\t%-4s\t%-5s\t%-20s\t%-10s\n", "名字", "年龄", "性别", "住址", "手机号");
    printf("%-20s\t%-4d\t%-5s\t%-20s\t%-10s\n", pc->data[pos].name,
                                                pc->data[pos].age,
                                                pc->data[pos].sex,
                                                pc->data[pos].addr,
                                                pc->data[pos].tele);

}

void ModifyContact(Contact* pc)
{
    assert(pc);
    char name[NAME_MAX] = { 0 };
    printf("请输入你想修改的人的名字:>");
    scanf("%s", name);
    int pos = FindByName(pc, name);
    if (-1 == pos)
    {
        printf("要修改的人不存在\n");
        return;
    }
    printf("请输入名字:>");
    scanf("%s", pc->data[pos].name);
    printf("请输入年龄:>");
    scanf("%d", &(pc->data[pos].age));
    printf("请输入性别:>");
    scanf("%s", pc->data[pos].sex);
    printf("请输入地址:>");
    scanf("%s", pc->data[pos].addr);
    printf("请输入电话:>");
    scanf("%s", pc->data[pos].tele);
    printf("修改成功\n");
}
//test.c

#define _CRT_SECURE_NO_WARNINGS 1


#include "contact.h"

void menu()
{
    printf("**************************************\n");
    printf("******    1.add       2.del     ******\n");
    printf("******    3.search    4.modify  ******\n");
    printf("******    5.show                ******\n");
    printf("******    0.exit                ******\n");
    printf("**************************************\n");
}



int main()
{
    int input = 0;
    //创建通讯录
    Contact con;
    //初始化通讯录
    InitContact(&con);//结构体地址传参
    do
    {
        menu();
        printf("请选择:>");
        scanf("%d", &input);
        switch (input)
        {
        case 1:
            AddContact(&con);
            break;
        case 2:
            DelContact(&con);
            break;
        case 3:
            SearchContact(&con);
            break;
        case 4:
            ModifyContact(&con);
            break;
        case 5:
            ShowContact(&con);
            break;
        case 6:
            break;
        case 0:
            printf("退出通讯录\n");
            break;
        default:
            printf("选择错误\n");
            break;
        }
    } while (input);
    return 0;
}

一些细节方面的补充

assert,由于使用时参数为指针,assert()可以判断指针是否非空,若为空,提醒程序员,便于找出bug。

const的使用,注意思考这步操作是否需要修改对应数值,若不需要,可用const修饰,使代码更健壮。值得注意的是,若传参有const修饰,与其对接的参数也应有const修饰,做到一一对应。

函数的封装,比如“找出这个人”这个操作,由于我们不论是删除、查找、修改都要用到这步操作,我们可以将其封装成一个函数。


后记

这篇博客只是从大体上写出了通讯录,仍有一些地方可以继续修改与完善,我会在接下来的几篇博客里慢慢补充。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值