您的位置:首页 > 财经 > 金融 > 上海外贸营销网站建设网站_网页制作基础教程代码_windows优化大师可靠吗_seo外包推广

上海外贸营销网站建设网站_网页制作基础教程代码_windows优化大师可靠吗_seo外包推广

2025/4/4 14:09:42 来源:https://blog.csdn.net/2401_87711435/article/details/145311889  浏览:    关键词:上海外贸营销网站建设网站_网页制作基础教程代码_windows优化大师可靠吗_seo外包推广
上海外贸营销网站建设网站_网页制作基础教程代码_windows优化大师可靠吗_seo外包推广

“我若成佛,天下无魔;我若成魔,佛奈我何。”

“小爷是魔,那又如何?”

下面我和一起来攻克通讯录的难关!!

在这里插入图片描述

明确通讯录的基本结构

实现一个通讯录:

人的信息:
名字+年龄+性别+电话+地址

实现通讯录的基本步骤

假设
1.存放100个人的信息
2.增加联系人
3.删除指定联系人
4.查找联系人
5.修改联系人
6.排序
7.显示联系人

首先我们要先创建三个文件

三个文件的功能如下:

  • test.c -> 测试功能
  • contact.c->通讯录相关的实现
  • contact.h->通讯录相关的声明

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.shor显示        6.sort排序   ********\n");printf("**********           0.exit退出             ********\n");printf("****************************************************\n");
}
int main()
{//使用do while结构打印一个菜单int input = 0;//创建通讯录Contact con;//通讯录(这个con就是通讯录)//初始化通讯录InitContact(&con);//要用指针接收//这里为什么要传地址(有两点原因)//1.结构体传参传地址更加准确//2.要修改这个通讯录就必须传的是地址(参考修改一个整数)do{menu();printf("请选择:>");scanf("%d", &input);switch (input){//由于枚举常量实际上就是整数,因此可以直接将input的值与枚举常量进行比较case ADD://增加人的信息AddContact(&con);break;case DEL://删除DelContact(&con);break;case SEARCH:SearchContact(&con);break;case MODIFY:ModifyContact(&con);break;case SORT:SortContact(&con);break;case PRINT:PrintContact(&con);//打印,不会改变conbreak;case EXIT:printf("退出通讯录\n");break;default:printf("选择错误,重新选择\n");break;}} while (input);return 0;
}

contact.h(类型的声明)

1.用结构体定义人的信息

//类型的声明
typedef struct PeoInfo
{char name[MAX_NAME];int age;char sex[MAX_SEX];char tele[MAX_TELE];char addr[MAX_ADDR];}PeoInfo;

2.封装一个通讯录

在contact.h中声明函数

void InitContact(Contact* pc);//前面取的是con的地址 所以用pc指针接收

在contact.c中实现此函数

typedef struct Contact
{PeoInfo data[MAX];//存放添加进来的人的信息,假设开辟100个空间int sz;//记录当前通讯录中有效信息的个数
}Contact;

这里说明通讯录中既有人的信息,又有人的个数

3.初始化通讯录

void InitContact(Contact* pc)
{pc->sz = 0;//memset();memset(pc->data, 0, sizeof(pc->data));//将pc指向的数组初始化为0//也就是将开辟的100个空间全部都初始化为0
}

补充一个知识:memset 的使用

memset用于将一段内存区域的每个字节都设置为指定的值

void *memset(void *s, int c, size_t n);
  • s:指向要填充的内存块的指针
  • c:要设置的值,该值以 int 类型传递,但在填充内存时会被转换为 unsigned char 类型。(例如初始化为几)
  • n:要填充的字节数

返回值指向内存块s指针

注意:
在contact.h中引入头文件#include<string.h>

4.增加联系人到通讯录中

声明:

void AddContact(Contact* pc);

实现:

void AddContact(Contact* pc)
{if (pc->sz == MAX){printf("通讯录已满,无法添加\n");return;}//增加一个人的信息printf("请输入名字:>");scanf("%s", pc->data[pc->sz].name);//这里指的是将一个人的信息存放到date数组下标为sz的数组里面(因为刚开始pc指向的是date数组)//数组名不需要取地址printf("请输入年龄:>");scanf("%d", &(pc->data[pc->sz].age));//这里的age是一个整形变量所以需要取地址printf("请输入性别:>");scanf("%s", pc->data[pc->sz].sex);printf("请输入电话:>");scanf("%s", pc->data[pc->sz].tele);printf("请输入地址:>");scanf("%s", pc->data[pc->sz].addr);pc->sz++;printf("增加成功\n");
}
  • 这里详细解释一下pc->data[pc->sz].name的意思

pc->data[pc->sz].name: pc 是指向Contact结构体的指针pc->data 访问的是Contact结构体中的data数组,pc->sz 表示当前已存储的联系人数量pc->data[pc->sz] 就是data数组中第pc->sz个元素(即当前要添加的联系人信息),pc->data[pc->sz].name 则是该联系人的姓名数组。由于数组名本身就代表数组首元素的地址,所以不需要取地址符 &

  • pc->sz++ 又是什么意思呢

pc->sz++ 是一个自增操作,它的作用是将Contact结构体中的sz成员的值加 1。在添加新联系人信息时,sz 表示当前已存储的联系人数量。当成功添加一个新联系人后,已存储的联系人数量就会增加 1,所以需要将sz的值加 1。这样,下次再添加联系人时,就会将新联系人的信息存储到 data 数组的下一个位置。

5.打印联系人信息

声明:

void ShorContact(const Contact* pc);

实现:

void ShorContact(const Contact* pc)
{assert(pc);//断言一下,以防空指针int i = 0;printf("%-20s\t%-5s\t%-5s\t%-12s\t%-30s\n", "名字", "年龄", "性别", "电话", "地址");//加负号表示左对齐for (i = 0; i < pc->sz; i++){printf("%-20s\t%-5d\t%-5s\t%-12s\t%-30s\n", pc->data[i].name,pc->data[i].age, pc->data[i].sex,pc->data[i].tele,pc->data[i].addr);}
}

注意:

  • pc->sz 表示当前Contact 结构体中已经存储的联系人数量
  • for 循环的条件i < pc->sz确保循环会遍历data数组中所有已存储的联系人信息。每次循环结束后,i 的值会加 1,直到i等于 pc->sz 时,循环停止
  • 这里的格式化字符串"%20s\t%3d\t%5s\t%12s\t%30s\n"详细解释如下:
    • %20s:表示以字符串形式输出,并且该字符串的最小宽度为 20 个字符。如果实际字符串长度小于 20,则会在左边补齐空格,用于输出联系人的姓名。
    • %5d:表示以十进制整数形式输出,最小宽度为 5 个字符,用于输出联系人的年龄。
    • %5s:表示以字符串形式输出,最小宽度为 5 个字符,用于输出联系人的性别。
    • %12s:表示以字符串形式输出,最小宽度为 12 个字符,用于输出联系人的电话号码。
    • %30s:表示以字符串形式输出,最小宽度为 30 个字符,用于输出联系人的地址。
    • \t 是制表符,用于在输出中分隔不同的信息字段,使输出更加整齐。
    • \n 是换行符,用于在输出完一个联系人的信息后换行,方便查看。

最后再详细说明一下作用:

  • pc->data 等价于 (*pc).data,表示访问pc所指向的Contact结构体中的data数组。
  • data[i] 表示 data 数组中的第i个元素,由于 data 数组的元素类型是 PeoInfo,所以 data[i] 就是一个 PeoInfo 结构体变量,代表第i个联系人的详细信息

6.删除联系人的信息

声明:

void DelContact(Contact* pc);

实现:

static int FindByName(Contact* pc, char name[])
{assert(pc);int i = 0;//i 将作为循环计数器,用于访问 pc->data 数组中的每个元素for (i = 0; i < pc->sz; i++)//遍历通讯录中的所有联系人{if (0 == strcmp(pc->data[i].name, name))//比较当前联系人的名字与要查找的名字是否相同{return i;//返回下标}}return -1;//没找到该联系人就返回-1
}
void DelContact(Contact* pc)
{char name[MAX_NAME] = { 0 };//定义一个字符数组 name,用于存储用户输入的要删除的联系人名字assert(pc);//断言 确保传入的 Contact* pc 指针是有效的int i = 0;if (pc->sz == 0)//检查通讯录是否为空{printf("通讯录为空,没有信息可以删除\n");return;}printf("请输入要删除人的名字:>");scanf("%s", name);//删除//1.查找int pos = FindByName(pc, name);//调用 FindByName 函数,在 Contact 结构体中查找指定姓名的联系人,并将返回的下标存储在 pos 变量中if (pos == -1)//检查 pos 是否为 -1。如果为 -1,说明未找到要删除的联系人{printf("要删除的人不存在\n");return;}//2.删除for (i = pos; i < pc->sz - 1; i++)//当找到要删除的联系人后,需要将该联系人后面的所有联系人信息依次向前移动一位,覆盖掉要删除的联系人信息。{pc->data[i] = pc->data[i + 1];//从 pos 位置开始,将 data 数组中 i + 1 位置的元素赋值给 i 位置,直到遍历到倒数第二个元素}pc->sz--;//删除联系人后,已存储的联系人数量减少 1,因此将 pc->sz 的值减 1printf("删除成功\n");
}

对上面这段代码进行详细分析:

  • 函数 FindByName功能概述:
    FindByName 函数的主要功能是在Contact结构体所存储的联系人信息中,根据输入的姓名查找对应的联系人,并返回该联系人在data数组中的下标。如果未找到匹配的联系人,则返回 -1
  • DelContact 函数
    DelContact 函数的主要功能是从Contact结构体所存储的联系人信息中删除指定姓名的联系人。

注意:
这里for (i = pos; i < pc->sz - 1; i++)为什么要sz-1?
原因:
pc->sz 表示当前通讯录中联系人的数量,因此最后一个联系人的索引是 pc->sz - 1(也就是下标)如果不减一的话就会造成越界访问

7.查找指定联系人

声明:

void SearchContact(Contact* pc);

实现:

void SearchContact(Contact* pc)
{assert(pc);char name[MAX_NAME] = { 0 };//MAX_NAME 是一个常量,表示名字的最大长度并将数组初始化为全 0printf("请输入要查找人的名字:>");scanf("%s", name);//1.查找int pos = FindByName(pc, name);//FindByName 函数会返回该联系人在 data 数组中的下标,如果未找到则返回 -1。将返回值存储在 pos 变量中if (pos == -1){printf("要查找的人不存在\n");return;//找不到就返回}//2.打印else{printf("%-20s\t%-5s\t%-5s\t%-12s\t%-30s\n", "名字", "年龄", "性别", "电话", "地址");//找到了就打印printf("%-20s\t%-5d\t%-5s\t%-12s\t%-30s\n", pc->data[pos].name,pc->data[pos].age,pc->data[pos].sex,pc->data[pos].tele,pc->data[pos].addr);}
}

上述代码的整体基本逻辑如下:

  1. 输入名字:
    提示用户输入要查找的联系人名字,并将其存储到name数组中。

  2. 查找联系人:
    调用FindByName函数,根据名字在通讯录中查找联系人。
    如果未找到,输出提示信息并结束函数。

  3. 打印联系人信息:
    如果找到联系人,格式化输出其详细信息(名字、年龄、性别、电话、地址)。

8.修改指定联系人

声明:

void ModifyContact(Contact* pc);

实现:

void ModifyContact(Contact* pc)
{assert(pc);char name[MAX_NAME] = { 0 };printf("请输入要修改人的名字:>");scanf("%s", name);//1.查找int pos = FindByName(pc, name);if (pos == -1){printf("要修改的人不存在\n");return;//找不到就返回}printf("要修改人的信息已查找到,接下来请开始修改\n");//2.修改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].tele);printf("请输入地址:>");scanf("%s", pc->data[pos].addr);printf("修改成功\n");}

基本逻辑

  1. 输入名字:
    提示用户输入要修改的联系人名字,并将其存储到 name 数组中。

  2. 查找联系人:
    调用 FindByName 函数,根据名字在通讯录中查找联系人。
    如果未找到,输出提示信息并结束函数。

  3. 修改联系人信息:
    如果找到联系人,提示用户依次输入新的名字、年龄、性别、电话和地址。
    将用户输入的信息更新到通讯录中。

  4. 提示修改成功:
    输出提示信息,表示联系人信息已成功修改。

9.排序通讯录的信息(可以按照名字排序也可以按照年龄来排序)

这里举的例子是按照名字来排序

int cmp_peo_by_name(const void* e1, const void* e2)
{return strcmp(((PeoInfo*)e1)->name, ((PeoInfo*)e2)->name);//将 void* 指针转换为 PeoInfo* 类型,然后访问 name 字段
}//按照名字来排序
void SortContact(Contact* pc)//Contact* pc: 指向通讯录结构体的指针,包含联系人数据
{assert(pc);qsort(pc->data, pc->sz, sizeof(PeoInfo), cmp_peo_by_name);//pc->data: 指向联系人数组的起始地址。//pc->sz: 联系人数组中元素的数量。//sizeof(PeoInfo): 每个联系人结构体的大小。//cmp_peo_by_name: 比较函数,用于定义排序规则。printf("排序成功\n");
}

补充知识
1.qsort 函数
qsort 是 C 标准库(<stdlib.h>)中提供的通用排序函数,用于对数组进行排序。

  • 函数原型:
void qsort(void* base, size_t nmemb, size_t size, int (*compar)(const void*, const void*));
  • 参数:

    • base: 指向数组的起始地址。
    • nmemb: 数组中元素的数量。
    • size: 每个元素的大小(以字节为单位)。
    • compar: 比较函数,用于定义排序规则。
  • 比较函数:
    比较函数接收两个 const void* 类型的参数,返回一个整数:

    • 负数:第一个参数小于第二个参数。
    • 0:两个参数相等。
    • 正数:第一个参数大于第二个参数。
  1. strcmp 函数
    strcmp 是 C 标准库(<string.h>)中提供的字符串比较函数,用于比较两个字符串的字典序。
  • 函数原型:
int strcmp(const char* str1, const char* str2);
  • 参数:

    • str1: 第一个字符串。
    • str2: 第二个字符串。
  • 返回值:

    • 负数:str1 小于 str2。
    • 0:str1 等于 str2。
    • 正数:str1 大于 str2。
  • 比较规则:

    • 按字符的 ASCII 值逐个比较。
    • 比较过程在遇到第一个不相等的字符或字符串结束符 \0 时停止。

10.contact.h的整体声明如下:

#pragma once
#include<string.h>
#include<assert.h>
#include<stdio.h>
#include<stdlib.h>
#define MAX 100# define MAX_NAME 20
#define MAX_SEX 10
#define MAX_TELE 12
#define MAX_ADDR 30//类型的声明
typedef struct PeoInfo
{char name[MAX_NAME];int age;char sex[MAX_SEX];char tele[MAX_TELE];char addr[MAX_ADDR];}PeoInfo;//通讯录
typedef struct Contact
{PeoInfo data[MAX];//存放添加进来的人的信息,假设开辟100个空间int sz;//记录当前通讯录中有效信息的个数
}Contact;//初始化通讯录
void InitContact(Contact* pc);//前面取的是con的地址 所以用pc指针接收//增加联系人
void AddContact(Contact* pc);//打印联系人信息
void ShowContact(const Contact* pc);//删除联系人的信息
void DelContact(Contact* pc);//查找指定联系人
void SearchContact(Contact* pc);//修改指定联系人
void ModifyContact(Contact* pc);//排序通讯录的信息
void SortContact(Contact* pc);

在这里插入图片描述

最后大体的整合一下就完成啦!
注意:
本章只是对通讯录的静态表示方式噢~

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com