结构体—简单通讯录实现

结构体——简单通讯录实现

1、结构体知识回顾

1.1、结构体的声明

struct tag
{
    menmber-list;
    
}variable-list;

描述一个学生:

struct Stu
{
    char name[20];
    int age;
    char sex[5];
    char id[20];
};  // 分号不要忘记

1.2、结构体变量的创建和初始化

#include<stdio.h>
struct Stu
{
    char name[20]; // 姓名
    int age;       // 年龄
    char sex[15];  // 性别
    char id[20];   // 学号
};

int main()
{
    // 按结构体成员顺序初始化
    struct Stu s ={"李四", 20, "男", "1088200066"};
    printf("姓名: %s\n", s.name);
    printf("年龄: %s\n", s.age);
    printf("性别: %s\n", s.sex);
    printf("学号: %s\n", s.id);
    
    // 按指定的顺序初始化
    struct Stu s ={.age = 18, .name = "李四", .sex = "男", .id = "1088200066"};
    printf("姓名: %s\n", s.name);
    printf("年龄: %s\n", s.age);
    printf("性别: %s\n", s.sex);
    printf("学号: %s\n", s.id);
    return 0;
}

1.3、结构特殊声明

声明结构的时候可以不完全声明。

比如:结构体在声明的时候省略结构体标签

# include<stdio.h>
int main()
{
    // 定义一个匿名结构体
    struct
    {
        int x;
        int y;
    }point1, point2; // point1和point2是该结构体的两个实例
    
    // point1和point2赋值
    point1.x =  10;
    point1.y = 20;
    
    point2.x = 10;
    point2.y = 20;
    
    // 输出
    printf("point1:(%d %d)\n", point1.x, point2.y);
    printf("point2:(%d %d)\n", point1.x, point2.y);
    
    return 0;
}

定义匿名结构体:

  • struct 关键字后没有结构体命名(tag),而是直接定义了结构体成员(x 和 y).
  • 这种结构体只能在定义他的作用域内实现。(匿名结构体的类型没有全局或外部可见性,只有在定义它的地方才能操作该结构体的实例)

优点:

  • 简洁性: 在某些情况下,使用匿名结构体可以使代码更加简洁,尤其是在只需要使用一次的情况下
  • 局部性: 匿名结构体的作用域限制在定义他的地方,避免了命名冲突。

1.4、结构的自引用

​ 在结构体中不能包含一个类型为该结构体本身的成员,因为当结构体中再包含一个同类型的结构体变量,会使得结构体变量的大小变得无穷大,这是不合理的。但是可以包含该结构体成员的地址。

struct Node
{
    int data;
    struct Node* next;
};

在结构体⾃引⽤使⽤的过程中,夹杂了 typedef 对匿名结构体类型重命名,也容易引⼊问题.

问题代码:

typedef struct
{
    int data;
    Node* next;
}Node;

分析原因: 原理上上述代码执行完成后才完成对匿名结构体类型的重命名,但是在重命名过程中便引入了Node, 这便引起了先有鸡还是先有蛋的问题,显然是不对的。

解决方案:定义结构体不要使用匿名结构体

typedef struct Node
{
    int data;
    struct Node* next;
};

2、 结构体内存对齐

2.1 对齐规则

  1. 结构体的第一个成员对齐到和结构体变量起始位置偏移量为0的地址处。
  2. 其他成员变量要对齐到对齐数 的整数倍的地址处。
  3. 对齐数 = 编译器默认的对齐数和成员变量大小的较小值。
  4. VS中默认的对齐数是8。
  5. Linux中gcc没有默认的对齐数,对齐数是成员变量的自身大小。
  6. 结构体总大小为最大对齐数 (结构体总每个成员变量都有一个对齐数,所有对齐数中最大的)的整数倍。
  7. 如果嵌套了结构体的话,嵌套的结构体成员对齐到自己的成员中最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数的(含嵌套结构体成员的对齐数)的整数倍。

2.2、修改默认对齐数

#pragma 这个预处理指令,可以改变编译器的默认对齐数。

# include<stdio.h>
#pragma pack(1) // 设置默认对齐数为1
struct S
{
    char c1;
    int i;
    char c2;
};
#pragma pack() // 回复默认对齐数

int main()
{
	printf("%d\n", sizeof(struct S));
    return 0;
}

3、结构体传参

结构体传参最好传地址

#include<stdio,h>
struct S
{
    int data[100];
    int num;
};  // 不要忘记;
//  结构体传参
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;
}

4、简单通讯录的实现

要求:

实现一个通讯录
人的信息:
名字+年龄+性别+电话+地址
存放100个人的信息、增加联系人、删除指定的练习人、查找练习人、修改联系人、排序、显示连续人

为了后期维护和 查看的方便我们将该项目写成多文件的形式。

test1.c main函数主程序

contact.c 各种函数的实现

contact.h 头文件和函数声明


test1.c

#include<stdio.h>

void menu()
{
	printf("******************************************\n");
	printf("******* 1.add              2.del    ******\n");
	printf("******* 3.search           4.modify ******\n");
	printf("******* 5.show             6.sort   ******\n");
	printf("******* 0.exit                      ******\n");
	printf("******************************************\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 6:
                break;
            case 0:
                printf("退出通讯录\n");
                break;
            default:
                printf("选择错误\n");
                break;
        }
    }while(input);
    return 0;
}

​ 首先,为实现多次输入我们想到利用 do....while 循环, 为了根据输入实现相应的功能,利用 switch 。根据多任务,我们打印的菜单如图所示。

​ 哦,对了,怎么能少了用结构体存放人的信息呢,我们着手子在 contact.h 中声明一个结构体类型

typedef struct PeoInfo
{
    char name[20];  // 姓名 
    int age;        // 年龄
    char sex[10];   // 性别
    char tele[12];  // 电话
    char addr[30]   // 地址
}PeoInfo;

可以存放人员信息的结构类型已经被我们声明好了,接下来我们要创建一个通讯录,同样创建通讯录我们放在 contact.h 中。

// 创建通讯录
typedef struct Contact
{
    PeoInfo data[100];  // 存放人的信息
    int count;          // 当前通讯录的实际的人数
}Contact;

用结构体实习那对人的信息的存放,然后创建一个通讯录,紧接着我们要对通讯录进行初始化,这是必须的。

我们设计一个函数来进行通讯录的初始化,同样在 contact.h 中声明函数, 在 contact.c 中实习那函数的功能。

contact.h

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

contact.c

void InitContact(Contact* pc)
{
    pc->count = 0;
    memset(pc->data,0,sizeof(pc->data)); // memset 函数将data数组所在内存全部u初始化为0;
}                                        // 头文件<string.h>

​ 因为牵扯到指针,为了避免野指针的出现,我们在之后的函数中,凡是牵扯到阴蒂指针进来的,都要进行assert断言, 头文件<assert.h>.

是为避免头文件的重复声明,我们将所有头文件都放在 `contact.h` 中,在 `test.1` 和 `contact.c` 中仅声明 `#include"contact.h"` 即可。

​ 同时。我么们在结构体中定义name的大小是20 ,后期我么们想要维护是不方便了的,因此我们采用宏定义的方法,在 contact.h 中,我么们定义如下:

#define MAX 100
#define MAX_NAME 20;
#define MAX_SEX 10;
#define MAX_TELE 12
#define MAX_ADDR 30

经过上述操作后,各文件的状态如下:

test1.c

#include"contact.h"

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

}

int main()
{
    int input = 0;
    
    Contact con; // 创建一个通讯录
    
    // 初始化通讯录
    InitContact(&con);
    
    do
    {
        menu();   // 打印菜单
        printf("请选择:>");
        scanf("%d", &input);
        switch(input)
        {
            case 1:
                break;
            case 2:
                break:
            case 3:
                break:
            case 4:
                break;
            case 5:
                break:
            case 6:
                break;
            case 0:
                printf("退出通讯录\n");
                break;
            default:
                printf("选择错误\n");
                break;
        }
    }while(input);
    return 0;
}

contact.c

# include"contact.h"

// 初始化通讯录
void InitContact(Contact* pc)
{
    assert(pc);
    pc->count = 0;
    memset(pc->data,0,sizeof(pc->data)); // memset 函数将data数组所在内存全部u初始化为0;
}                                        // 头文件<string.h>

contact.h

# include<stdio.h>
# include<assert.h>
# include<string.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[20];  // 姓名 
    int age;        // 年龄
    char sex[10];   // 性别
    char tele[12];  // 电话
    char addr[30]   // 地址
}PeoInfo;

// 创建通讯录
typedef struct Contact
{
    PeoInfo data[100];  // 存放人的信息
    int count;          // 当前通讯录的实际的人数
}Contact;

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

创建和初始化通讯录的工作都已经完成了,之后,我们开始编写相关的功能函数。

首先 增加联系人函数AddContact()

// 增加联系人
void AddContact(Contact* pc)// 函数声明
// 怎加联系人函数
void AddContact(Contact* pc)
{
    assert(pc);
    if(pc->count == MAX)
    {
        printf("通讯录已满,无法继续添加\n");
        return;
    }
    printf("请输入名字:>");
    scanf("%s", pc->data[pc->count].name);
    printf("请输入年龄:>");
    scanf("%d", &(pc->data[pc->count].age));
    printf("请输入性别:>");
    scanf("%s", pc->data[pc->count].sex);
    printf("请输入电话:>");
    scanf("%s", pc->data[pc->count].tele);
    printf("请输入地址:>");
    scanf("%s", pc->data[pc->count].addr);
    
    pc->count++;
    printf("增加成功\n");
}

显示通讯录函数: ShowContact()

//显示通讯录
void ShowContact(Contact* pc); // 函数声明
// 显示通讯录函数
void ShowContact(Contact* pc)
{
    assert(pc);
    printf("%-15s\t%-15s\t-15s\t-15s\t-15s\t-15s\n", "姓名:""年龄:", "性别:", "电话", "地址:");
    int i = 0;
    for(i = 0; i < pc->count; i++)
    {
        printf("%-15s\t%-15d\t%-15s\t%-15s\t%-15s\n", pc->data[pc->count].name,
                                                      pc->data[pc->count].age,
                                                      pc->data[pc->count].sex,
                                                      pc->data[pc->count].tele,
                                                      pc->data[pc->count].addr,);
    }
}

删除联系人函数: DeleContact()

// 删除联系人函数
void DeleContact(Contact* pc)
int FindByNmae(Contact* pc, char name[])
{
    assert(pc);
    int i = 0;
    for(i = 0; i < pc->count; i++)
    {
        if(strcmp(pc->data[i].name, name))
        {
            return i;  // 返回下标
        }
    }
    return -1;
}

void DeleContact(Contact* pc)
{
    assert(pc);
    int i = 0;
    char name[MAX_name] = {0};
    if(pc->count == 0)
    {
        printf("通讯录为空,没有联系人可以删除\n");
        return;
    }
    printf("请输入要删除人的名字:>");
    scanf("%s", name);
  	// 要删除首先需要查找
    // 查找
    int pos = FindByName(pc,name); // 没有找到返回-1, 找到的话返回下标
   	if(-1 == pos)
    {
        printf("你要删除的联系人通讯录中不存在。\n");
        return;
    }
    
    // 删除
    for(i = pos, i < pc->count-1; i++)  // 为了防止越界,count - 1
    {
        pc->data[i] = pc->count[i+1];  // 从后向前覆盖
    }
    pc->count--;
   	printf("删除成功,\n");
    
}

搜索联系人函数: SearchContact()

// 搜索联系人函数
void SearchContact(Contact* pc);
void SearchContact(Contact* pc)
{
    assert(pc);
    int i = 0;
    char name[MAX_name] = {0};
    if(pc->count == 0)
    {
        printf("通讯录为空,没有联系人可以查找\n");
        return;
    }
    printf("请输入查找人的名字:>");
    scanf("%s", name);
    // 查找
    int pos = FindByName(pc,name); // 没有找到返回-1, 找到的话返回下标
   	if(-1 == pos)
    {
        printf("你要查找的联系人通讯录中不存在。\n");
        return;
    }
    
    // 打印
     printf("%-15s\t%-15s\t-15s\t-15s\t-15s\t-15s\n", "姓名:""年龄:", "性别:", "电话", "地址:");
    int i = 0;
	printf("%-15s\t%-15d\t%-15s\t%-15s\t%-15s\n", pc->data[pos].name,
                                                      pc->data[pos].age,
                                                      pc->data[pos].sex,
                                                      pc->data[pos].tele,
                                                      pc->data[pos].addr,);
}

修改联系人函数: MoifyContact()

//修改联系人函数
void ModifyContact(Contact* pc);    
// 修改联系人函数
void ModifyContact(Contact* pc)
{
    assert(pc);
    
    // 想要修改首先需要先找到
    int i = 0;
    char name[MAX_name] = {0};
    if(pc->count == 0)
    {
        printf("通讯录为空,没有联系人可以查找\n");
        return;
    }
    printf("请输入查找人的名字:>");
    scanf("%s", name);
    // 查找
    int pos = FindByName(pc,name); // 没有找到返回-1, 找到的话返回下标
   	if(-1 == pos)
    {
        printf("你要查找的联系人通讯录中不存在。\n");
        return;
    }
    
    // 找到后再重新打印
    printf("请重新输入想要修改的联系人信息:>\n");
    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");
}

排序函数: SortContact()

// 排序函数
void SortContact(Contact* pc);
int cmp_peo_by_name(const void* e1, const void* e2)
{
    return strcmp((PeoInfo*)e1->name, (PeoInfo*)e2->name);
}
void SortContact(Contact* pc)
{
    assert(pc);
    // 使用qsort函数进行排序
    qsort(pc->data, pc->count, sizeof(PeoInfo), cmp_peo_By_name);
    printf("排序成功\n");
}

到这里,所有的函数已经完成

个文件现在的状态是:

test1.c

#include"contact.h"

void menu()
{
	printf("******************************************\n");
	printf("******* 1.add              2.del    ******\n");
	printf("******* 3.search           4.modify ******\n");
	printf("******* 5.show             6.sort   ******\n");
	printf("******* 0.exit                      ******\n");
	printf("******************************************\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:
			DeleContact(&con);
			break;
		case 3:
			SearchContact(&con);
			break;
		case 4:
			ModifyContact(&con);
			break;
		case 5:
			ShowContact(&con);
			break;
		case 6:
			SortContact(&con);
			break;
		case 0:
			printf("退出通讯录\n");
			break;
		default:
			printf("选择错误\n");
			break;
		}
	} while (input);
	return 0;
}

contact.c

#include"contact.h"

// 初始化通讯录
void InitContact(Contact* pc)
{
	assert(pc);
	pc->count = 0;
	memset(pc->data, 0, sizeof(pc->data));
}


// 增加联系人函数
void AddContact(Contact* pc)
{
	assert(pc);
	if (pc->count == MAX)
	{
		printf("通讯录已满,无法添加\n");
		return;
	}
	printf("请输入名字:>");
	scanf("%s", pc->data[pc->count].name);
	printf("请输入年龄:>");
	scanf("%d", &(pc->data[pc->count].age));
	printf("请输入性别:>");
	scanf("%s", pc->data[pc->count].sex);
	printf("请输入电话:>");
	scanf("%s", pc->data[pc->count].tele);
	printf("请输入地址:>");
	scanf("%s", pc->data[pc->count].addr);


	pc->count++;
	printf("增加成功\n");
}


// 显示通讯录的讯息
void ShowContact(const Contact* pc)
{
	assert(pc);
	int i = 0;
	printf("%-20s\t%-5s\t%-5s\t%-12s\t%-30s\n", "名字", "年龄", "性别", "电话", "地址");
	for (i = 0; i < pc->count; 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);
		}
}

// 查找函数(我们在头文件中欸有暴露出去)
int FindByName(Contact* pc, char name[])
{
	assert(pc);
	int i = 0;
	for (i = 0; i < pc->count; i++)
	{
		if (0 == strcmp(pc->data[i].name, name))
		{
			return i;
		}
	}
	return -1;
}



// 删除指定的连续人
void DeleContact(Contact* pc)
{
	assert(pc);
	int i = 0;
	char name[MAX_NAME] = { 0 };
	if (pc->count == 0)
	{
		printf("通讯录为空,没有讯息可以删除\n");
	}
	printf("请输入要删除的人的名字:>");
	scanf("%s", name);

	// 要先删除首先需要查找
	// 1、查找  
	int pos = FindByName(pc, name);  // 找到的话返回下标,找不到的话返回-1
	if (-1 == pos)
	{
		printf("要删除的人不存在\n");
		return;
	}
	// 2、删除 
	for (i = pos; i < pc->count-1; i++)  // count-1是为了防止数组越界
	{
		pc->data[i] = pc->data[i + 1];  // 将后面的元素往前覆盖
	}
	pc->count--;

	printf("删除成功\n");
}


// 目录中的查找函数
void SearchContact(const Contact* pc)
{
	assert(pc);
	int i = 0;
	char name[MAX_NAME] = { 0 };
	printf("请输入查找的人的名字:>");
	scanf("%s", name);

	// 1、查找  
	int pos = FindByName(pc, name);  // 找到的话返回下标,找不到的话返回-1
	if (-1 == pos)
	{
		printf("要查找的人不存在\n");
		return;
	}
	// 2、打印
	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);
}

// 修改指定的联系人
void ModifyContact(Contact* pc)
{
	assert(pc);
	int i = 0;
	char name[MAX_NAME] = { 0 };
	printf("请输入修改的人的名字:>");
	scanf("%s", name);

	// 1、查找  
	int pos = FindByName(pc, name);  // 找到的话返回下标,找不到的话返回-1
	if (-1 == pos)
	{
		printf("要修改的人不存在\n");
		return;
	}
	// 2、修改
	printf("请对要修改的人的信息重新录入:>\n");
	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");
}



// qsort函数中需要的函数
int cmp_peo_by_name(const void* e1, const void* e2)
{
	return strcmp(((PeoInfo*)e1)->name, ((PeoInfo*)e2)->name);
}
// 目录中的排序函数(按照名字排序)
void SortContact(Contact* pc)
{
	assert(pc);
	qsort(pc->data, pc->count, sizeof(PeoInfo), cmp_peo_by_name);
	printf("排序成功\n");
}

contact.h

#pragma once

#include<string.h>
#include<stdlib.h>
#include<stdio.h>
#include<assert.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]; // 存放人的讯息
	int count; // 记录当前通讯录中实际的个数
}Contact;

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

// 增加讯息
void AddContact(Contact* pc);

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

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

// 查找函数
void SearchContact(const Contact* pc);

// 修改指定的联系人
void ModifyContact(Contact* pc);

// 排序(按照名字排序)
void SortContact(Contact* pc);

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值