前言------学习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;
}
主函数部分就没什么需要讲的地方了,以上就是我在编写通讯录时的心得体会,如果能帮助到你的话,实在是再好不过了。
上述代码纯属小白练手作品,希望大佬海涵 。