Teach you how to write address book (including dynamic version)

Table of contents

1. Framework

Two, realize

1. Initialize the address book

2. Add contacts

3. Print address book

4. Delete contacts

5. Modify contacts

6. Find contacts

7. Exit the address book

8. Expansion: address book sorting

 9. Full code

3. Dynamic version

1. Structure modification

2. Initial modification 

3. Implementation of capacity expansion

 4. Aftermath function

 5. Full code


1. Framework

Before implementing the address book, let's think about what functions our address book needs to have. Learn from the phone's built-in address book, the function of the address book must first be able to store various information of contacts such as: name, gender, age, contact information, address and so on. This time, on this basis, it is necessary to realize the deletion, search, modification, etc. of the contacts stored in the address book.

Now that we have an idea, let’s create a framework first     and implement a menu with printf.     This menu can help us use the address book more conveniently, and it can be understood as a guide function. Next, we can use switch to realize the function selection.    Secondly , we want to keep going on this address book until the user doesn’t want to use it anymore, so write a loop.     To sum up, we can write the code like this

#include<stdio.h>
void menu()
{
		printf("**********************************************\n");
		printf("**********************************************\n");
		printf("*******1.增加联系人******2.删除联系人*********\n");
		printf("*******3.修改联系人******4.查找联系人*********\n");
		printf("*******5.打印通讯录******0.退出通讯录*********\n");
		printf("**********************************************\n");
		printf("**********************************************\n");
}
int main()
{
	int choose = 0;
	do
	{
        menu();
		scanf("%d", &choose);
		switch (choose)
		{
		case 1:
			//调用增加联系人的函数
			break;
		case 2:
			//调用删除联系人的函数
			break;
		case 3:
			//调用修改联系人信息的函数
			break;
		case 4:
			//调用查找联系人的函数
			break;
        case 5:
             //调用打印通讯录的函数
            break;
		case 0:
			//调用退出函数
			break;
		default:
			printf("语法错误,请重新输入\n");
			break;
		}
	} while (1);
	//写成无限循环,只能通过输入0退出循环
}

 The function has not been realized yet, it is just a framework, and then we think about it, there can not be only one contact, there will be many,  so we should use an array to store , and the information contained in the contact also    has a lot of name, age, gender , contact information, address, etc., an ordinary array can't fit at all, and creating multiple arrays to store them separately is very cumbersome. So we should create a contact structure    so that we can create the corresponding structure array, that is, the contact array, which will be much more convenient to operate.

#include<stdio.h>
struct people
{
	char name[10];//姓名存放的是字符串,故用char
	int age;     //年龄存放的是一个数字,故用int
	char sex[5];//与名字的原因相同
	char tel[12];//与名字的原因相同
	char address[15];//与名字的原因相同
};

However, if we want a name that can store 15 elements in the future, we have to change 10 from the beginning to the end, which is very inconvenient, so here we adopt the macro definition method and define several global constants, so that we can Realize that one hair can move the whole body

#include<stdio.h>
#define name_max 10
#define sex_max 5
#define tel_max 12
#define address_max 15
struct people
{
	char name[name_max];//姓名存放的是字符串,故用char
	int age;     //年龄存放的是一个数字,故用int
	char sex[sex_max];//与名字的原因相同
	char tel[tel_max];//与名字的原因相同
	char address[address_max];//与名字的原因相同
};

Continue to think, the structure of the contact has been defined, should we create a corresponding structure array and then think about how to add, delete, and modify the structure array, almost to this point. But our framework is a little bit worse. At the beginning, our address book must not have a single contact, we need to store contacts in it, and every time you store a contact, it will increase the number of contacts in the corresponding contact array, and store it next time When contacting, the subscript corresponding to the array must fluctuate by one. Can we create a variable to store how many contacts are stored    ? If there is no contact at the beginning, then this variable will be 0, corresponding to the first in the array The subscript of the element is taken directly when the storage operation is performed, and after one is stored, the variable is +1, corresponding to the target subscript to be stored next time. But to create such a variable separately, it seems inconvenient that there will be an extra step when the function passes parameters. If    we pass a variable, this variable is not just an array of contacts, but an array of contacts and stored contacts. The number is just fine    , so we can create another structure called address book, and include both of them    so that we can realize the function we need by passing one when calling the function to pass parameters.

#include<stdio.h>
#define name_max 10
#define sex_max 5
#define tel_max 12
#define address_max 15
#define people_max 100//最多能存放100个联系人
typedef struct people
{
	char name[name_max];//姓名存放的是字符串,故用char
	int age;     //年龄存放的是一个数字,故用int
	char sex[sex_max];//与名字的原因相同
	char tel[tel_max];//与名字的原因相同
	char address[address_max];//与名字的原因相同
}peo;//来个别名,使用方便
typedef struct contact
{
	peo data[people_max];
	//结构体成员名为data,它是可以存放100个struct people类型元素的数组
	int sz;
	//已经存放了几个联系人
}con;//别名,使用方便

The framework is basically ready here, and the next step is to implement

Two, realize

1. Initialize the address book

Before doing all operations, we should first create a variable of the address book type (the one we created before that combines the member array and the number of storage), and then initialize it. The function of initializing the address book can be separately packaged into a function , the return type of our function is an empty type, because we only use it to initialize the address book, of course, if you want, you can design its return type as a pointer variable of the address book, so that the return can be realized at that time The first address of the address book implements the chain rule and operates.

void init_contact(con* c1)//传址调用,实现修改
{
	assert(c1);
	//断言,避免传空指针,使用这个函数需要引assert.h头文件
	memset(c1->data,0,sizeof(c1->data));
	//c1->data,指向的是数组名,代表着数组首元素的地址
	//sizeof(c1->data)代表计算整个数组所占的字节数
	//0代表把给定地址往后的sizeof(c1->data)个字节都初始化为0
	//memset位于stdlib.h头文件中
	c1->sz = 0;//把c1指向的sz初始化为0
}

2. Add contacts

After initializing the contacts, all our preparatory actions are finally completed, and then we will talk about how to add contacts. In fact, it is very simple. With the subscript (sz, that is, the number of contacts stored in the current address book), you can naturally find the target to be operated. Access the members of the structure through the arrows     and find the target through sz. After finding the target, use scanf to modify them.    One thing to note is that when we add contacts, the address book is already full and obviously cannot be placed. Yes, if you put it again, it is an out-of-bounds access, so we should make a judgment. When the stored contacts reach the upper limit, don't save them.

void add(con*c1)
{
	assert(c1);//断言防止传空指针
	if (c1->sz == people_max)
	{
		printf("通讯录已满,存放失败\n");
		//存放失败就直接返回,由于是空类型,所以直接return
		return;
	}
	else//通讯录没满就往里存
	{
		printf("请输入联系人姓名\n");
		scanf("%s",c1->data[c1->sz].name);//字符串变量名就是首地址
		printf("请输入联系人年龄\n");
		scanf("%d", &(c1->data[c1->sz].age));//年龄是整型,得取地址
		printf("请输入联系人性别\n");
		scanf("%s", c1->data[c1->sz].sex);
		printf("请输入联系人联系方式\n");
		scanf("%s", c1->data[c1->sz].tel);
		printf("请输入联系人家庭住址\n");
		scanf("%s", c1->data[c1->sz].address);
		printf("添加成功\n");
		c1->sz += 1;//联系人增多一名
	}
}

Don’t forget to call the written function in the switch statement 

3. Print address book

After implementing the function of adding contacts, you will definitely feel weird. I added it as if I didn’t add it, and I can’t see it. Then let’s implement a function of printing the address book, so that you can see what we added. After entering the information, think about the return type first, just print the content of the address book, do not do other operations, continue to use the empty type, the parameter part is still the variable pointer of the address book structure (with this thing, all the contents of the address book are can be found), so that our function can already be realized, but we can design it more perfectly, our goal is to print the content of the address book, and will not modify the content, so we can put it before the * number Add a const modification, so that the content pointed to by the address book structure variable pointer cannot be modified.

void print_contact(const con* c1)
{
	assert(c1);//断言避免传空指针
	int i = 0;
	if (c1->sz == 0)
	{
		printf("通讯录未存放联系人\n");
		return;
	}
	printf("%-10s\t%-5s\t%-5s\t%-12s\t%-15s\t\n", "姓名", "年龄", "性别", "联系方式", "家庭住址");
	//根据自己的喜好对齐,使信息更加明了
	for (i = 0; i < c1->sz; i++)//写个循环,因为可能不止一个联系人
	{
		printf("%-10s\t%-5d\t%-5s\t%-12s\t%-15s\t\n",
			c1->data[i].name,
			c1->data[i].age,
			c1->data[i].sex,
			c1->data[i].tel,
			c1->data[i].address);
	}
}

Don’t forget to call the written function in the switch statement  

4. Delete contacts

To delete a contact, we use the same return type as the empty type , and the parameter is also a pointer to the address book structure variable, but because it needs to be modified, it does not need to be modified with const. We can enter the name, and then pass strcmp and loop. The number of loops is obviously the number of current contacts. It is equivalent to traversing the address book to find out whether there is a target person. If it exists, it will be correspondingly downloaded. The mark is stored, and if it does not exist, just return it directly. After finding the target, the next step is to delete. If you delete, you can delete it by overwriting . For example, the subscript of the contact I want to delete is 2, which is the third element, and I have stored 5 members. Then we Just cover the fourth element on the third element, cover the fifth element on the fourth element, and then delete sz--, the theory exists, start to practice

void con_del(con* c1)//删除联系人
{
	assert(c1);
	if (c1->sz == 0)
	{
		printf("通讯录为空,操作失败\n");
		return;
	}
	char a1[name_max] = { 0 };//初始化
	printf("请输入你要删除联系人的姓名\n");
	scanf("%s", &a1);
	int i = 0; int flaw = 0;
	for (i = 0; i < c1->sz; i++)
	{
		if (strcmp(a1, c1->data[i].name) == 0)
		{
			flaw = 1;//用来分析是否查找到目标
			break;//找到下标,退出循环
		}
	}
	if (flaw == 0)
	{
		printf("查无此人,删除失败\n");
		return;
	}
	for (; i < c1->sz-1; i++)
	//为什么是sz-1呢,避免越界操作,最后一个元素其实我们是可以不用管它的
    //因为sz--之后你下一次增加联系人的时候就会把它覆盖掉
	//且你不增加联系人的话,你肯定再也访问不到这个下标对应的元素了
	{
		c1->data[i] = c1->data[i+1];
	}
	printf("删除成功\n");
	c1->sz--;
}

 After implementing this part, the author feels that the function of finding the target subscript is very convenient and useful. It can be used when we do the search and print function, so we pack this part of the function into a function, let it find the subscript of the target for us by name

int find(const con* c1)
//要有返回值来判断是否找到目标
//只是寻找不修改,用const修饰
{
	char a1[name_max] = { 0 };//初始化
	scanf("%s", &a1);
	int i = 0;
	for (i = 0; i < c1->sz; i++)
	{
		if (strcmp(a1, c1->data[i].name) == 0)
		{
			return i;//找到目标,直接返回下标,注意下标可能为0
		}
	}
	return -1;//找不到目标,返回-1
}
void con_del(con* c1)//删除联系人
{	
    assert(c1);
	if (c1->sz == 0)
	{
		printf("通讯录为空,操作失败\n");
		return;
	}
	printf("请输入你要删除的联系人的姓名\n");
	int i = find(c1);
	if (i!=-1)
	{//找到的话返回非0的数,为真,进入删除,没找到返回0,为假,不进入删除
		for (; i < c1->sz - 1; i++)
			//为什么是sz-1呢,避免越界操作,最后一个元素其实我们是可以不用管它的
			//因为sz--之后你下一次增加联系人的时候就会把它覆盖掉
			//且你不增加联系人的话,你肯定再也访问不到这个下标对应的元素了
		{
			c1->data[i] = c1->data[i + 1];
		}
		printf("删除成功\n");
		c1->sz--;
	}
	else
	{
		printf("查无此人,操作失败\n");
		return;
	}
}

5. Modify contacts

It is easy to modify the contact. With the previous function to find the corresponding contact, we can quickly find the target subscript, that is to say, we only need to change the corresponding thing.

void dif(con* c1)
{
	assert(c1);
	if (c1->sz == 0)
	{
		printf("通讯录为空,操作失败\n");
		return;
	}
	else {
		printf("请输入你要修改的联系人的姓名\n");
		int i = find(c1);
		if (i != -1)
		{
			printf("请输入被修改后的姓名\n");
			scanf("%s", c1->data[i].name);
			printf("请输入被修改后的年龄\n");
			scanf("%d", &c1->data[i].age);
			printf("请输入被修改后的性别\n");
			scanf("%s", c1->data[i].sex);
			printf("请输入被修改后的联系方式\n");
			scanf("%s", c1->data[i].tel);
			printf("请输入被修改后的地址\n");
			scanf("%s", c1->data[i].address);
			printf("修改成功\n");
			return;
		}
		else
		{
			printf("查无此人,操作失败\n");
			return;
		}
	}
}

6. Find contacts

This uses our subscript lookup function, find the subscript and print it one more step

void search(con* c1)
{
	assert(c1);
	if (c1->sz == 0)
	{
		printf("通讯录为空,操作失败\n");
		return;
	}
	else
	{
		printf("请输入你要查找的联系人姓名\n");
		int i=find(c1);
		if(i!=-1)
		{
			printf("%-10s\t%-5s\t%-5s\t%-12s\t%-15s\t\n", "姓名", "年龄", "性别", "联系方式", "家庭住址");
			printf("%-10s\t%-5d\t%-5s\t%-12s\t%-15s\t\n",
				c1->data[i].name,
				c1->data[i].age,
				c1->data[i].sex,
				c1->data[i].tel,
				c1->data[i].address);
			return;
		}
		else
		{
			printf("查无此人,操作失败\n");
			return;
		}
	}
}

7. Exit the address book

The simplest one is here, the exit() function is enough

8. Expansion: address book sorting

When we store contacts, we may come here randomly, just put whoever comes to mind. Over time, the address book will become disorganized, so here the author specially wrote a function of sorting the address book, which uses the qsort function. If you don’t understand the qsort function very well, you can read the article written by the author to teach you how to use the qsort function http://t.csdn.cn/wSMoW

Don't forget to put the function in the switch statement, and reorganize the menu guidance operation. 

Basic

int cmp_age(const void*ptr1,const void*ptr2 )
{
	return ((peo*)ptr1)->age - ((peo*)ptr2)->age;//升序排序
}
void sort(const con*c1)
{
	qsort(c1->data,c1->sz, sizeof(peo),cmp_age);
	//c1->data是存放联系人的结构体数组首元素的地址
	//c1->sz是要排的人数//sizeof(peo)为联系人结构体数组一个元素的大小
	//cmp_age是需要我们自己设计的函数
	//如何设计看笔者之前的文章,这里就不再赘述
	printf("排序成功\n");
}

 Upgraded version

int cmp_age(const void*ptr1,const void*ptr2 )
{
	return ((peo*)ptr1)->age - ((peo*)ptr2)->age;//升序排序
}
int cmp_name(const void* ptr1, const void* ptr2)
{
	return strcmp(((peo*)ptr1)->name,((peo*)ptr2)->name);
}
int cmp_tel(const void* ptr1, const void* ptr2)
{
	return strcmp(((peo*)ptr1)->tel,((peo*)ptr2)->tel);
}
void sort(const con* c1)
{
	int choose = 0;
	do {
		printf("输入0退出排序\n");
		printf("输入1按照年龄排序\n");
		printf("输入2按照姓名排序\n");
		printf("输入3按照电话号码排序\n");
		scanf("%d", &choose);
		switch (choose)
		{
		case 1:
			qsort(c1->data, c1->sz, sizeof(peo), cmp_age);
			//c1->data是存放联系人的结构体数组首元素的地址
			//c1->sz是要排的人数//sizeof(peo)为联系人结构体数组一个元素的大小
			//cmp_age是需要我们自己设计的函数
			//如何设计看笔者之前的文章,这里就不再赘述
			printf("排序成功\n");
			break;
		case 2:
			qsort(c1->data, c1->sz, sizeof(peo), cmp_name);
			printf("排序成功\n");
			break;
		case 3:
			qsort(c1->data, c1->sz, sizeof(peo), cmp_tel);
			printf("排序成功\n");
			break;
		case 0:
			return;
			break;
		default:
			printf("语法错误,请重新输入\n");
			break;
		}
	} while (1);//无限循环除非choose为0
}

 9. Full code

They are all written in one file by the author. In fact, it is better to divide them. For example, one file contains the declaration of the function, one file implements the function, and one file implements the operation.

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>
#define name_max 10
#define sex_max 5
#define tel_max 12
#define address_max 15
#define people_max 100//最多能存放100个联系人
typedef struct people
{
	char name[name_max];//姓名存放的是字符串,故用char
	int age;     //年龄存放的是一个数字,故用int
	char sex[sex_max];//与名字的原因相同
	char tel[tel_max];//与名字的原因相同
	char address[address_max];//与名字的原因相同
}peo;//来个别名,使用方便
typedef struct contact
{
	peo data[people_max];
	//结构体成员名为data,它是可以存放100个struct people类型元素的数组
	int sz;
	//已经存放了几个联系人
}con;//别名,使用方便
void menu()
{
		printf("**********************************************\n");
		printf("**********************************************\n");
		printf("*******1.增加联系人******2.删除联系人*********\n");
		printf("*******3.修改联系人******4.查找联系人*********\n");
		printf("*******5.打印通讯录******6.通讯录排序*********\n");
		printf("*******7.退出通讯录***************************\n");
		printf("**********************************************\n");
}
void init_contact(con* c1)//初始化通讯录
{
	assert(c1);
	//断言,避免传空指针,使用这个函数需要引assert.h头文件
	memset(c1->data,0,sizeof(c1->data));
	//c1->data,指向的是数组名,代表着数组首元素的地址
	//sizeof(c1->data)代表计算整个数组的字节数
	//0代表把给定地址往后的sizeof(c1->data)个字节都初始化为0
	//memset位于stdlib.h头文件中
	c1->sz = 0;//把c1指向的sz初始化为0
}
void add(con*c1)//增加联系人
{
	assert(c1);//断言防止传空指针
	if (c1->sz == people_max)
	{
		printf("通讯录已满,存放失败\n");
		//存放失败就直接返回,由于是空类型,所以直接return
		return;
	}
	else//通讯录没满就往里存
	{
		printf("请输入联系人姓名\n");
		scanf("%s",c1->data[c1->sz].name);//字符串变量名就是首地址
		printf("请输入联系人年龄\n");
		scanf("%d", &(c1->data[c1->sz].age));//年龄是整型,得取地址
		printf("请输入联系人性别\n");
		scanf("%s", c1->data[c1->sz].sex);
		printf("请输入联系人联系方式\n");
		scanf("%s", c1->data[c1->sz].tel);
		printf("请输入联系人家庭住址\n");
		scanf("%s", c1->data[c1->sz].address);
		printf("添加成功\n");
		c1->sz += 1;//联系人增多一名
	}
}
void print_contact(const con* c1)//打印通讯录
{
	assert(c1);//断言避免传空指针
	int i = 0;
	if (c1->sz == 0)
	{
		printf("通讯录未存放联系人\n");
		return;
	}
	printf("%-10s\t%-5s\t%-5s\t%-12s\t%-15s\t\n", "姓名", "年龄", "性别", "联系方式", "家庭住址");
	//根据自己的喜好对齐,使信息更加明了
	for (i = 0; i < c1->sz; i++)//写个循环,因为可能不止一个联系人
	{
		printf("%-10s\t%-5d\t%-5s\t%-12s\t%-15s\t\n",
			c1->data[i].name,
			c1->data[i].age,
			c1->data[i].sex,
			c1->data[i].tel,
			c1->data[i].address);
	}
}
int find(const con* c1)
//要有返回值来判断是否找到目标
//只是寻找不修改,用const修饰
{
	char a1[name_max] = { 0 };//初始化
	scanf("%s", &a1);
	int i = 0;
	for (i = 0; i < c1->sz; i++)
	{
		if (strcmp(a1, c1->data[i].name) == 0)
		{
			return i;//找到目标,直接返回下标,注意下标可能为0
		}
	}
	return -1;//找不到目标,返回-1
}
void con_del(con* c1)//删除联系人
{
	assert(c1);
	if (c1->sz == 0)
	{
		printf("通讯录为空,操作失败\n");
		return;
	}
	printf("请输入你要删除的联系人的姓名\n");
	int i = find(c1);
	if (i!=-1)
	{//找到的话返回非0的数,为真,进入删除,没找到返回0,为假,不进入删除
		for (; i < c1->sz - 1; i++)
			//为什么是sz-1呢,避免越界操作,最后一个元素其实我们是可以不用管它的
			//因为sz--之后你下一次增加联系人的时候就会把它覆盖掉
			//且你不增加联系人的话,你肯定再也访问不到这个下标对应的元素了
		{
			c1->data[i] = c1->data[i + 1];
		}
		printf("删除成功\n");
		c1->sz--;
	}
	else
	{
		printf("查无此人,操作失败\n");
		return;
	}
}
void dif(con* c1)
{
	assert(c1);
	if (c1->sz == 0)
	{
		printf("通讯录为空,操作失败\n");
		return;
	}
	else {
		printf("请输入你要修改的联系人的姓名\n");
		int i = find(c1);
		if (i != -1)
		{
			printf("请输入被修改后的姓名\n");
			scanf("%s", c1->data[i].name);
			printf("请输入被修改后的年龄\n");
			scanf("%d", &c1->data[i].age);
			printf("请输入被修改后的性别\n");
			scanf("%s", c1->data[i].sex);
			printf("请输入被修改后的联系方式\n");
			scanf("%s", c1->data[i].tel);
			printf("请输入被修改后的地址\n");
			scanf("%s", c1->data[i].address);
			printf("修改成功\n");
			return;
		}
		else
		{
			printf("查无此人,操作失败\n");
			return;
		}
	}
}
void search(con* c1)
{
	assert(c1);
	if (c1->sz == 0)
	{
		printf("通讯录为空,操作失败\n");
		return;
	}
	else
	{
		printf("请输入你要查找的联系人姓名\n");
		int i=find(c1);
		if(i!=-1)
		{
			printf("%-10s\t%-5s\t%-5s\t%-12s\t%-15s\t\n", "姓名", "年龄", "性别", "联系方式", "家庭住址");
			printf("%-10s\t%-5d\t%-5s\t%-12s\t%-15s\t\n",
				c1->data[i].name,
				c1->data[i].age,
				c1->data[i].sex,
				c1->data[i].tel,
				c1->data[i].address);
			return;
		}
		else
		{
			printf("查无此人,操作失败\n");
			return;
		}
	}
}
int cmp_age(const void*ptr1,const void*ptr2 )
{
	return ((peo*)ptr1)->age - ((peo*)ptr2)->age;//升序排序
}
int cmp_name(const void* ptr1, const void* ptr2)
{
	return strcmp(((peo*)ptr1)->name,((peo*)ptr2)->name);
}
int cmp_tel(const void* ptr1, const void* ptr2)
{
	return strcmp(((peo*)ptr1)->tel,((peo*)ptr2)->tel);
}
void sort(const con* c1)
{
	int choose = 0;
	do {
		printf("输入0退出排序\n");
		printf("输入1按照年龄排序\n");
		printf("输入2按照姓名排序\n");
		printf("输入3按照电话号码排序\n");
		scanf("%d", &choose);
		switch (choose)
		{
		case 1:
			qsort(c1->data, c1->sz, sizeof(peo), cmp_age);
			//c1->data是存放联系人的结构体数组首元素的地址
			//c1->sz是要排的人数//sizeof(peo)为联系人结构体数组一个元素的大小
			//cmp_age是需要我们自己设计的函数
			//如何设计看笔者之前的文章,这里就不再赘述
			printf("排序成功\n");
			break;
		case 2:
			qsort(c1->data, c1->sz, sizeof(peo), cmp_name);
			printf("排序成功\n");
			break;
		case 3:
			qsort(c1->data, c1->sz, sizeof(peo), cmp_tel);
			printf("排序成功\n");
			break;
		case 0:
			return;
			break;
		default:
			printf("语法错误,请重新输入\n");
			break;
		}
	} while (1);//无限循环除非choose为0
}
int main()
{
	int choose = 0;
	con c1;
	init_contact(&c1);
	do
	{
	    menu();
		scanf("%d", &choose);
		switch (choose)
		{
		case 1:
			add(&c1);//调用增加联系人的函数
			break;
		case 2:
			con_del(&c1);//调用删除联系人的函数
			break;
		case 3:
			dif(&c1);//调用修改联系人信息的函数
			break;
		case 4:
			search(&c1);//调用查找联系人的函数
			break;
		case 5:
			print_contact(&c1);//调用打印通讯录的函数
			break;
		case 6:
			sort(&c1);//调用排序函数
			break;
		case 0:
			exit(1);//调用退出函数
			break;
		default:
			printf("语法错误,请重新输入\n");
			break;
		}
	} while (1);
	//写成无限循环,只能通过输入0退出循环
}

3. Dynamic version

1. Structure modification

The dynamic version is implemented through a series of dynamic memory management functions such as malloc, realloc and free. If you don’t know much about this aspect, you can refer to the author’s previous article to teach you how to use memory functions http: //t.csdn .cn/2hntB​​​​​​

The first thing to modify is our structure members. We used arrays to realize the number of members before, but in the c environment, the definition of arrays requires constants to create arrays, so it is immutable. Now we use pointers The way to achieve an "array" that can dynamically allocate memory according to our needs, and then the size of the capacity is very important, it can determine whether it needs to be expanded

typedef struct contact
{
	peo* data;
	//使用指针的方式
	int sz;
	//已经存放了几个联系人
	int capacity;
	//总容量的大小
}con;//别名,使用方便

2. Initial modification 

void init_contact(con* c1)//初始化通讯录
{

	c1->sz = 0;
	c1->capacity = people_max;
	c1->data =(peo*)malloc(sizeof(peo)*people_max);
	//开辟数组
	if (c1->data == NULL)
	{
		perror("init_contact");
		//开辟失败,错误提示
		exit(1);
	}
}

3. Implementation of capacity expansion

void if_bigger(con* c1)
{
	if (c1->capacity == c1->sz)
   //当容量和存放的个数相等,空间不够,得扩容
	{
		c1->data = (peo*)realloc(c1->data,sizeof(peo)*(c1->capacity + 2));
		//扩容之后比之前的容量大2,按自己喜欢调节
		if (c1->data == NULL)
		{
			perror("if_bigger");
			//开辟失败,错误提示
			return;
		}
		else
		{
			c1->capacity += 2;//开辟成功容量+2
			printf("开辟成功,当前可存放联系人个数为%d\n", c1->capacity);
		}
	}
}

add in contact

void add(con*c1)//增加联系人
{
	assert(c1);//断言防止传空指针
	if_bigger(c1);//放在前面,每次来都先检测一下容量是否够
	if (c1->sz == c1->capacity)
 //这个也要修改,现在是和容量比,之前是和people_max比
	{
		printf("通讯录已满,存放失败\n");
		//存放失败就直接返回,由于是空类型,所以直接return
		return;
	}
	else//通讯录没满就往里存
	{
		printf("请输入联系人姓名\n");
		scanf("%s",c1->data[c1->sz].name);//字符串变量名就是首地址
		printf("请输入联系人年龄\n");
		scanf("%d", &(c1->data[c1->sz].age));//年龄是整型,得取地址
		printf("请输入联系人性别\n");
		scanf("%s", c1->data[c1->sz].sex);
		printf("请输入联系人联系方式\n");
		scanf("%s", c1->data[c1->sz].tel);
		printf("请输入联系人家庭住址\n");
		scanf("%s", c1->data[c1->sz].address);
		printf("添加成功\n");
		c1->sz += 1;//联系人增多一名
	}
}

Here the author adjusts people_max to 3 to reduce the initial capacity, which is convenient for testing the expansion effect

 4. Aftermath function

This thing is used to destroy the address book, just put it in the exit section

void destroy_contact(con*c1)
{
	c1->capacity = 0;
	c1->sz = 0;
	free(c1->data);//释放空间
	c1->data = NULL;//野指针变空指针
}

 5. Full code

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>
#define name_max 10
#define sex_max 5
#define tel_max 12
#define address_max 15
#define people_max 3
typedef struct people
{
	char name[name_max];//姓名存放的是字符串,故用char
	int age;     //年龄存放的是一个数字,故用int
	char sex[sex_max];//与名字的原因相同
	char tel[tel_max];//与名字的原因相同
	char address[address_max];//与名字的原因相同
}peo;//来个别名,使用方便
//typedef struct contact
//{
//	peo data[people_max];
//	//结构体成员名为data,它是可以存放100个struct people类型元素的数组
//	int sz;
//	//已经存放了几个联系人
//}con;//别名,使用方便
typedef struct contact
{
	peo* data;
	//使用指针的方式
	int sz;
	//已经存放了几个联系人
	int capacity;
	//总容量的大小
}con;//别名,使用方便
void destroy_contact(con*c1)
{
	c1->capacity = 0;
	c1->sz = 0;
	free(c1->data);//释放空间
	c1->data = NULL;//野指针变空指针
}
void menu()
{
		printf("**********************************************\n");
		printf("**********************************************\n");
		printf("*******1.增加联系人******2.删除联系人*********\n");
		printf("*******3.修改联系人******4.查找联系人*********\n");
		printf("*******5.打印通讯录******6.通讯录排序*********\n");
		printf("*******7.退出通讯录***************************\n");
		printf("**********************************************\n");
}
void init_contact(con* c1)//初始化通讯录
{
	//assert(c1);
	断言,避免传空指针,使用这个函数需要引assert.h头文件
	//memset(c1->data,0,sizeof(c1->data));
	c1->data,指向的是数组名,代表着数组首元素的地址
	sizeof(c1->data)代表计算整个数组的字节数
	0代表把给定地址往后的sizeof(c1->data)个字节都初始化为0
	memset位于stdlib.h头文件中
	//c1->sz = 0;//把c1指向的sz初始化为0
	c1->sz = 0;
	c1->capacity = people_max;
	c1->data =(peo*)malloc(sizeof(peo)*people_max);
	//开辟数组
	if (c1->data == NULL)
	{
		perror("init_contact");
		//开辟失败,错误提示
		exit(1);
	}
}
void if_bigger(con* c1)
{
	if (c1->capacity == c1->sz)//容量和存放的个数相等,显然空间不够
	{
		c1->data = (peo*)realloc(c1->data,sizeof(peo)*(c1->capacity + 2));
		//扩容之后比之前的容量大2
		if (c1->data == NULL)
		{
			perror("if_bigger");
			//开辟失败,错误提示
			return;
		}
		else
		{
			c1->capacity += 2;//开辟成功容量+2
			printf("开辟成功,当前可存放联系人个数为%d\n", c1->capacity);
		}
	}
}
void add(con*c1)//增加联系人
{
	assert(c1);//断言防止传空指针
	if_bigger(c1);//放在前面,每次来都先检测一下容量是否够
	if (c1->sz == c1->capacity)//这个也要修改,现在是和容量比
	{
		printf("通讯录已满,存放失败\n");
		//存放失败就直接返回,由于是空类型,所以直接return
		return;
	}
	else//通讯录没满就往里存
	{
		printf("请输入联系人姓名\n");
		scanf("%s",c1->data[c1->sz].name);//字符串变量名就是首地址
		printf("请输入联系人年龄\n");
		scanf("%d", &(c1->data[c1->sz].age));//年龄是整型,得取地址
		printf("请输入联系人性别\n");
		scanf("%s", c1->data[c1->sz].sex);
		printf("请输入联系人联系方式\n");
		scanf("%s", c1->data[c1->sz].tel);
		printf("请输入联系人家庭住址\n");
		scanf("%s", c1->data[c1->sz].address);
		printf("添加成功\n");
		c1->sz += 1;//联系人增多一名
	}
}
void print_contact(const con* c1)//打印通讯录
{
	assert(c1);//断言避免传空指针
	int i = 0;
	if (c1->sz == 0)
	{
		printf("通讯录未存放联系人\n");
		return;
	}
	printf("%-10s\t%-5s\t%-5s\t%-12s\t%-15s\t\n", "姓名", "年龄", "性别", "联系方式", "家庭住址");
	//根据自己的喜好对齐,使信息更加明了
	for (i = 0; i < c1->sz; i++)//写个循环,因为可能不止一个联系人
	{
		printf("%-10s\t%-5d\t%-5s\t%-12s\t%-15s\t\n",
			c1->data[i].name,
			c1->data[i].age,
			c1->data[i].sex,
			c1->data[i].tel,
			c1->data[i].address);
	}
}
int find(const con* c1)
//要有返回值来判断是否找到目标
//只是寻找不修改,用const修饰
{
	char a1[name_max] = { 0 };//初始化
	scanf("%s", &a1);
	int i = 0;
	for (i = 0; i < c1->sz; i++)
	{
		if (strcmp(a1, c1->data[i].name) == 0)
		{
			return i;//找到目标,直接返回下标,注意下标可能为0
		}
	}
	return -1;//找不到目标,返回-1
}
void con_del(con* c1)//删除联系人
{
	assert(c1);
	if (c1->sz == 0)
	{
		printf("通讯录为空,操作失败\n");
		return;
	}
	printf("请输入你要删除的联系人的姓名\n");
	int i = find(c1);
	if (i!=-1)
	{//找到的话返回非0的数,为真,进入删除,没找到返回0,为假,不进入删除
		for (; i < c1->sz - 1; i++)
			//为什么是sz-1呢,避免越界操作,最后一个元素其实我们是可以不用管它的
			//因为sz--之后你下一次增加联系人的时候就会把它覆盖掉
			//且你不增加联系人的话,你肯定再也访问不到这个下标对应的元素了
		{
			c1->data[i] = c1->data[i + 1];
		}
		printf("删除成功\n");
		c1->sz--;
	}
	else
	{
		printf("查无此人,操作失败\n");
		return;
	}
}
void dif(con* c1)
{
	assert(c1);
	if (c1->sz == 0)
	{
		printf("通讯录为空,操作失败\n");
		return;
	}
	else {
		printf("请输入你要修改的联系人的姓名\n");
		int i = find(c1);
		if (i != -1)
		{
			printf("请输入被修改后的姓名\n");
			scanf("%s", c1->data[i].name);
			printf("请输入被修改后的年龄\n");
			scanf("%d", &c1->data[i].age);
			printf("请输入被修改后的性别\n");
			scanf("%s", c1->data[i].sex);
			printf("请输入被修改后的联系方式\n");
			scanf("%s", c1->data[i].tel);
			printf("请输入被修改后的地址\n");
			scanf("%s", c1->data[i].address);
			printf("修改成功\n");
			return;
		}
		else
		{
			printf("查无此人,操作失败\n");
			return;
		}
	}
}
void search(con* c1)
{
	assert(c1);
	if (c1->sz == 0)
	{
		printf("通讯录为空,操作失败\n");
		return;
	}
	else
	{
		printf("请输入你要查找的联系人姓名\n");
		int i=find(c1);
		if(i!=-1)
		{
			printf("%-10s\t%-5s\t%-5s\t%-12s\t%-15s\t\n", "姓名", "年龄", "性别", "联系方式", "家庭住址");
			printf("%-10s\t%-5d\t%-5s\t%-12s\t%-15s\t\n",
				c1->data[i].name,
				c1->data[i].age,
				c1->data[i].sex,
				c1->data[i].tel,
				c1->data[i].address);
			return;
		}
		else
		{
			printf("查无此人,操作失败\n");
			return;
		}
	}
}
int cmp_age(const void*ptr1,const void*ptr2 )
{
	return ((peo*)ptr1)->age - ((peo*)ptr2)->age;//升序排序
}
int cmp_name(const void* ptr1, const void* ptr2)
{
	return strcmp(((peo*)ptr1)->name,((peo*)ptr2)->name);
}
int cmp_tel(const void* ptr1, const void* ptr2)
{
	return strcmp(((peo*)ptr1)->tel,((peo*)ptr2)->tel);
}
void sort(const con* c1)
{
	int choose = 0;
	do {
		printf("输入0退出排序\n");
		printf("输入1按照年龄排序\n");
		printf("输入2按照姓名排序\n");
		printf("输入3按照电话号码排序\n");
		scanf("%d", &choose);
		switch (choose)
		{
		case 1:
			qsort(c1->data, c1->sz, sizeof(peo), cmp_age);
			//c1->data是存放联系人的结构体数组首元素的地址
			//c1->sz是要排的人数//sizeof(peo)为联系人结构体数组一个元素的大小
			//cmp_age是需要我们自己设计的函数
			//如何设计看笔者之前的文章,这里就不再赘述
			printf("排序成功\n");
			break;
		case 2:
			qsort(c1->data, c1->sz, sizeof(peo), cmp_name);
			printf("排序成功\n");
			break;
		case 3:
			qsort(c1->data, c1->sz, sizeof(peo), cmp_tel);
			printf("排序成功\n");
			break;
		case 0:
			return;
			break;
		default:
			printf("语法错误,请重新输入\n");
			break;
		}
	} while (1);//无限循环除非choose为0
}
int main()
{
	int choose = 0;
	con c1;
	init_contact(&c1);
	do
	{
	    menu();
		scanf("%d", &choose);
		switch (choose)
		{
		case 1:
			add(&c1);//调用增加联系人的函数
			break;
		case 2:
			con_del(&c1);//调用删除联系人的函数
			break;
		case 3:
			dif(&c1);//调用修改联系人信息的函数
			break;
		case 4:
			search(&c1);//调用查找联系人的函数
			break;
		case 5:
			print_contact(&c1);//调用打印通讯录的函数
			break;
		case 6:
			sort(&c1);
			break;
		case 0:
			destroy_contact(&c1);//摧毁通讯录
			exit(1);//调用退出函数
			break;
		default:
			printf("语法错误,请重新输入\n");
			break;
		}
	} while (1);
	//写成无限循环,只能通过输入0退出循环
}

Today's sharing is over here, thank you friends for visiting, I wish you a bright future O(∩_∩)O

おすすめ

転載: blog.csdn.net/fq157856469/article/details/131758518
おすすめ