C语言使用动态顺序表实现通讯录管理系统

      前言------学习c语言也有小半年了,虽然早已拥有实现这个系统的能力,但碍于时间关系以及我的惰性,一直没有把这个系统的实现提上日程。趁着最近学习热情高涨,我想借实现这个简单系统的机会,检查我对顺序表的知识是否掌握的牢固,以及分享一下我在编写这个系统时所发现的问题。

学妹镇楼       

学妹镇楼 

      正文------

      这是系统的声明部分

#pragma once
#include<iostream>
#include<cassert>
#include<cstdlib>
#include<cstring>
using namespace std;

void menu();//打印菜单
typedef struct People//创建用户信息定义
{
	char name[20];
	int age;
	char sex[10];
	char num[20];
	char address[50];
}people;
typedef struct SListNode//顺序表
{
	people* p;
	int size;
	int capacity;
}ST;

void InitST(ST* s);//初始化顺序表
void BuyST(ST* s);//扩大顺序表
void AddST(ST* s);//添加用户
void PrintST(ST* s);//打印用户信息
void deleteST(ST* s);//删除用户
void modST(ST* s);//修改用户
void freeST(ST* s);//格式化

    我编写的时候对函数的命名没下太大功夫,如果代码风格不合你的口味,还请多海涵。

    这里我想分享一下我在定义SListNode结构体时,一度想要用二重指针定义p数组,就像这样

typedef struct SListNode//顺序表
{
	people** p;
	int size;
	int capacity;
}ST;

至于我为什么想这样做,而我为什么又放弃了这种行为,我将在后文解释。

----------函数实现

#define _CRT_SECURE_NO_WARNINGS 1
#include "SList.h"

void menu()
{
	cout << "**********************************" << '\n';
	cout << "******1、添加联系人***************" << '\n';
	cout << "******2、显示联系人***************" << '\n';
	cout << "******3、删除联系人***************" << '\n';
	cout << "******4、修改联系人***************" << '\n';
	cout << "******5、格式化通讯录*************" << '\n';
	cout << "******0、退出通讯录***************" << '\n';
	cout << "**********************************" << '\n';
}
void InitST(ST* s)
{
	s->p = (people*)malloc(sizeof(people) * 4);
	if (s->p == NULL)
	{
		perror("malloc fail");
		return;
	}
	s->capacity = 4;
	s->size = 0;

}
void BuyST(ST* s)
{
	assert(s);
	if (s->size - 1 == s->capacity)
	{
		people* tem = (people*)realloc(s->p, sizeof(people) * s->capacity * 2);
		if (tem == NULL)
		{
			perror("realloc fail");
			return;
		}
		s->p = tem;
		s->capacity = s->capacity * 2;
		cout << "通讯录扩大成功" << endl;
	}
}

void AddST(ST* s)
{
	assert(s);
	BuyST(s);

	char name[20];
	cout << "请输入用户姓名" << endl;
	cin >> name;
	strcpy(s->p[s->size].name, name);

	int age = 0;
	cout << "请输入用户年龄" << endl;
	cin >> age;
	s->p[s->size].age = age;
	cout << "请输入用户性别" << endl;
	char sex[10];
	cin >> sex;
	strcpy(s->p[s->size].sex, sex);

	cout << "请输入用户电话号码" << endl;
	char num[20];
	cin >> num;
	strcpy(s->p[s->size].num, num);
	cout << "请输入用户住址" << endl;
	char address[50];
	cin >> address;
	strcpy(s->p[s->size].address, address);
	s->size++;
	cout << "添加成功" << endl;
}
void PrintST(ST* s)
{
	if (s->size == 0)
	{
		cout << "通讯录为空" << endl;
	}
	for (int i = 0; i < s->size; i++)
	{
		cout << "用户姓名:" << s->p[i].name << '\t'
			<< "用户年龄:" << s->p[i].age << '\t'
			<< "用户性别" << s->p[i].sex << '\t'
			<< "用户电话:" << s->p[i].num << '\t'
			<< "用户住址:" << s->p[i].address << endl;
	}
}

void deleteST(ST* s)
{
	cout << "请输入您想删除的用户" << endl;
	char name[20];
	cin >> name;
	int pos = 0;
	while (strcmp(s->p[pos].name, name) != 0)
	{
		pos++;
		if (pos == s->size)
		{
			cout << "查无此人" << endl;
			return;
		}
	}
	if (pos == s->size - 1)//此时顺序表只有一个用户或者想删除的用户在表尾
	{
		s->size--;
		cout << "删除成功" << endl;
	}
	else
	{
		for (int i = pos; i < s->size; i++)
		{
			if (i == s->size - 1)
			{
				break;
			}
			s->p[i] = s->p[i + 1];
		}
		s->size--;
		cout << "删除成功" << endl;
	}
}
void modST(ST* s)
{
	cout << "请输入您想查找的用户" << endl;
	char _name[20];
	cin >> _name;
	int pos = 0;
	while (strcmp(s->p[pos].name, _name) != 0)
	{
		pos++;
		if (pos == s->size)
		{
			cout << "查无此人" << endl;
			return;
		}
	}
	cout << "请重新输入用户信息" << endl;
	char name[20];
	cout << "请输入用户姓名" << endl;
	cin >> name;
	strcpy(s->p[pos].name, name);

	int age = 0;
	cout << "请输入用户年龄" << endl;
	cin >> age;
	s->p[pos].age = age;
	cout << "请输入用户性别" << endl;
	char sex[10];
	cin >> sex;
	strcpy(s->p[pos].sex, sex);

	cout << "请输入用户电话号码" << endl;
	char num[20];
	cin >> num;
	strcpy(s->p[pos].num, num);
	cout << "请输入用户住址" << endl;
	char address[50];
	cin >> address;
	strcpy(s->p[pos].address, address);
	cout << "修改成功" << endl;
}
void freeST(ST* s)
{
	free(s->p);
	s->size = 0;
	s->capacity = 4;
}

如果读者对顺序表的增删查找功能掌握的比较熟练的话,理解以上代码应该不是一件难事,甚至你可以写的比我更好。

但可以注意到的是,我是用了部分c++的语法来代替C语言中比较繁琐的scanf和printf,如果接触过简单c++的话,应该能轻易理解cin ,cout以及"<<"">>"运算符重载的用法

接下来请让我逐一分享一下我编写各段函数时的浅薄心得

首先是初始化部分

void InitST(ST* s)
{
	s->p = (people*)malloc(sizeof(people) * 4);
//这里我给通讯录开了4块空间,如果想自己决定开多少空间,可以在传参时多传一个参数
	if (s->p == NULL)
	{
		perror("malloc fail");
		return;
	}
	s->capacity = 4;
	s->size = 0;

}

接下来的addST函数才是促使我从二重指针改为动态结构体数组的决定性因素

void AddST(ST* s)
{
	assert(s);
	BuyST(s);

	char name[20];
	cout << "请输入用户姓名" << endl;
	cin >> name;
//如果给字符串赋值时采用直接赋值时,编译器将报错,因为结构体里的name是不可修改的左值,权限放大了
//所以我才用了strcpy函数直接吧name赋值给给用户
	strcpy(s->p[s->size].name, name);

	int age = 0;
	cout << "请输入用户年龄" << endl;
	cin >> age;
	s->p[s->size].age = age;
	cout << "请输入用户性别" << endl;
	char sex[10];
	cin >> sex;
	strcpy(s->p[s->size].sex, sex);

	cout << "请输入用户电话号码" << endl;
	char num[20];
	cin >> num;
	strcpy(s->p[s->size].num, num);
	cout << "请输入用户住址" << endl;
	char address[50];
	cin >> address;
	strcpy(s->p[s->size].address, address);
	s->size++;//最后别忘了size++
//这里应该注意的是++后的size指向的是此时数组里的下一个元素,在使用size下标时应该注意这点
	cout << "添加成功" << endl;
}

如果我创建了二级指针的话,我在调用结构体时就会变成以下形式

s->p[s->size]->name

看到这里时聪明的读者可能已经发现了,作为最里面的name从始至终都没有被开辟过空间,

如果我们执意要用二级指针的话,我们就要单独为每个字符串单独开辟空间,还得为一级指针也开辟空间,这样写实在是太麻烦,不如干脆用一级指针开辟空间就好。

删除功能跟修改功能差别不大,这里我只讲解删除功能

void deleteST(ST* s)
{
	cout << "请输入您想删除的用户" << endl;
	char name[20];
	cin >> name;
	int pos = 0;
	while (strcmp(s->p[pos].name, name) != 0)
	{
		pos++;//定义pos指针下标查找想要删除的用户
		if (pos == s->size)//前面的size++伏笔终于回收了。如果pos已经指向了此时的size,说明查找不到该人物,直接返回就好
		{
			cout << "查无此人" << endl;
			return;
		}
	}
	if (pos == s->size - 1)//此时顺序表只有一个用户或者想删除的用户在表尾
	{
		s->size--;
		cout << "删除成功" << endl;
	}
	else
	{
		for (int i = pos; i < s->size; i++)
		{
			if (i == s->size - 1)//防止数组越界,不得不加
			{
				break;
			}
			s->p[i] = s->p[i + 1];//让顺序表从后往前覆盖数组,避免覆盖
		}
		s->size--;
		cout << "删除成功" << endl;
	}
}

------------主函数部分

int main()
{
	menu();
	ST s;
	InitST(&s);
	int input = 0;
	while (1)
	{
		cin >> input;
		switch (input)
		{
		case 1:
			AddST(&s);
			break;
		case 2:
			PrintST(&s);
			break;
		case 3:
			deleteST(&s);
		case 4:
			modST(&s);
			break;
		case 5:
			freeST(&s);
			break;
		case 0:
			cout << "退出程序" << endl;
			return 0;
		default:
			cout << "输入有误,请重新输入" << endl;
		}
	}
	return 0;
}

主函数部分就没什么需要讲的地方了,以上就是我在编写通讯录时的心得体会,如果能帮助到你的话,实在是再好不过了。

上述代码纯属小白练手作品,希望大佬海涵 。

 

 

 

 

  • 14
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值