玩转结构体

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


结构体、枚举、联合体

结构体

结构

结构就是一些值的集合,每个值都称为成员变量。每个成员都可以是不同的变量。

1、常规结构体

结构声明示例如下:

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

//比如定义一本书 :书名+作者+定价
struct book
{
	char book_name [20];
	char author[15];
	float price;
};

2、匿名结构体

可以省略结构体名字
但是必须创建在结构体之后
示例如下:

struct 
{
	char book_name [20];
	char author[15];
	float price;
}s1,s2;

3、结构体自引用

示例如下

struct Node
{
	int data ;
	struct Node* n;//不加*号会出现格式错误,是不能自己访问自己的。
}

4 、结构体初始化

struct Book
{
	char book_name [20];
	char author[15];
	float price;
}s1={"《平凡的世界》","莫言",85.5};
int main()
{
	struct Book s2={"西游记","罗贯中",35.6};
	printf("%s %s %.1f",s1.book_name,s1.author,s1.price);
}

5 、结构体内存对齐

计算结构体大小
计算对齐规则

1 、结构体的第一个成员直接对齐到结构体变量相对于起始位置为0的偏移处
2、从第二个成员开始,要对齐到某个【对齐数】的整数倍的偏移处。
对齐数:结构体成员自身大小和默认对齐数的较小值
VS:8
Linux: (结构体成员自身大小)
3、结构体的总大小,必须是最大对齐数的整数倍。
每个结构体成员都有一个对齐数,其中最大的对齐数就是最大对齐数。
4、如果嵌套结构体的情况。
嵌套的结构体对齐自己的最大对齐数的整数倍处,
结构体的整数倍大小就是所有最大对齐数
(含嵌套结构体的对齐数)的整数倍

示例如下:在这里插入图片描述

6、设置默认对齐数

//设置默认对齐数
#pragma pack(4)


//恢复默认对齐数
#pragma pack()

7、结构体传参

建议结构体传参采用传址调用,可以大大节省空间和时间开销

struct S
{
    int data[1000];
    int num;
};

void print1(struct S s)
{
   printf("%d %d %d %d\n", s.data[0], s.data[1], s.data[2], s.num);
}

void print2(const struct S* ps)
{
   //printf("%d %d %d %d\n", (*ps).data[0], (*ps).data[1], (*ps).data[2], (*ps).num);
    printf("%d %d %d %d\n", ps->data[0], ps->data[1], ps->data[2], ps->num);
}

int main()
{
    struct S ss = { {1,2,3,4,5}, 100 };
    print1(ss);
    print2(&ss);

    return 0;
}

8、结构体的应用

实例:
通讯录的实现
功能:
1、添加联系人
2、删除联系人
3、显示通讯录
4、查找联系人
5、修改联系人
6、排序联系人

代码实现思路
创建三个模块:主函数测试模块、通讯录头文件模块、通讯录功能函数实现模块
1、利用结构体可以收集不同变量要素的功能。
1.1创建通讯录

//一个人的信息
struct PeoInfo
{
	char name[NAME_MAX];
	char sex[SEX_MAX];
	char tele[TELE_MAX];
	int age;
	char addr[ADDR_MAX];
};

//多人信息组合构成整个通讯录
struct contact
{
	struct PeoInfo date [NUM_MAX];
	int sz;//表示通讯录中还没有信息

	//n个信息
	//再增加一个信息就放到下标为n的位置上
};

1.2利用memset库函数初始化通讯录

//正式使用之前需要对里面的信息初始化
void InitContact(struct contact* pc)
{
	pc->sz = 0;
	memset(pc->date, 0, NUM_MAX * sizeof(struct PeoInfo));
}

2、打印选项菜单,利用switch函数进行功能选择。通讯录是多次使用型,我们选择使用do while循环来执行多次操作。

//菜单显示函数
void menu()
{
	printf("************************************\n");
	printf("************************************\n");
	printf("*********1.add      2.delt   *******\n");
	printf("*********3.modify   4.search *******\n");
	printf("*********5.sort     6.show   *******\n");
	printf("*********0.exit              *******\n");
	printf("************************************\n");
	printf("************************************\n");
}

//测试函数主体框架
int main()
{
	int input;
	//创建通讯录
	struct contact con;
	//初始化通讯录
	InitContact(&con);
	//进行通讯录操作
	do 
	{
		menu();//打印菜单界面
		printf("请选择模式>:\n");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			AddContact(&con);//
			break;
		case 2:
			DeltContact(&con);
			break;
		case 3:
			ModifyContact(&con);
			break;
		case 4:
			SearchContact(&con);
			break;
		case 5:
			SortContact(&con);
			break;
		case 6:
			ShowContact(&con);
			break;
		case 0:
			printf("退出通讯录\n");
			break;
		default :
			printf("输入错误,请重新选择\n");
			break;

		}
	
	} while (input);
	return 0;
}

3、不同功能实现函数
3.1 、增加联系人
首先需要判断通讯录里面是否已经存满了,满了则返回空并输出通讯录已满;没满的情况下存入联系人,并将人数计数增加1。

void AddContact(struct contact* pc)
{
	assert(pc);
	if (pc->sz == NUM_MAX)
	{
		printf("通讯录已满\n");
		return;
	}
	printf("请输入联系人的名字>:");
	scanf("%s", pc->date[pc->sz].name);
	printf("请输入联系人的性别>:");
	scanf("%s", pc->date[pc->sz].sex);
	printf("请输入联系人的电话>:");
	scanf("%s", pc->date[pc->sz].tele);
	printf("请输入联系人的年龄>:");
	scanf("%d", &(pc->date[pc->sz].age));
	printf("请输入联系人的地址>:");
	scanf("%s", pc->date[pc->sz].addr);
	pc->sz++;
	printf("成功增加联系人\n");
}

3.2 、删除联系人
首先输入要删除联系人的名字,再遍历通讯录看联系人是否存在
查找联系人函数

//查找联系人函数
static int Find_name(const struct contact* pc, char* name)//static只是让在此.c文件能看见
{
	int i = 0;
	for (i = 0; i < pc->sz; i++)
	{
		if (0 == strcmp(pc->date[i].name, name))
		{
			return i;
		}
	}
	return -1;
}

再删除指定联系人

void DeltContact(struct contact* pc)
{
	char name[NAME_MAX];
	printf("请输入要删除联系人的名字>:");
	scanf("%s", name);
	//需要查找扫描通讯录
	int ret=Find_name(pc,name);
	if (-1 == ret)
	{
		printf("此联系人不存在\n");
	}
	else
	{
		int j = 0;
		for (j = ret; j < pc->sz-1; j++)
		{
			pc->date[j] = pc->date[j + 1];
		}
		pc->sz--;
		printf("成功指定删除联系人\n");
	}
}

3.3、显示通讯录
先要打印输出框架目录,在对应输出联系人信息

void ShowContact(const struct contact* pc) 
{
	printf("%-20s\t%-5s\t%-12s\t%-2s\t%-30s\n", "姓名", "性别", "电话","年龄", "地址");
	int i = 0;
	for (i = 0; i < pc->sz; i++)
	{
		printf("%-20s\t%-5s\t%-12s\t%-2d\t%s\n", pc->date[i].name,
			pc->date[i].sex,
			pc->date[i].tele,
			pc->date[i].age, 
			pc->date[i].addr);
	}
}

3.4、查找联系人
利用查找函数查找对应名字,再对应输出联系人信息

void SearchContact(const struct contact* pc)
{
	char name[NAME_MAX];
	printf("请输入查找的姓名\n");
	scanf("%s", name);
	int ret = Find_name(pc, name);
	if (-1 == ret)
	{
		printf("此联系人不存在\n");
	}
	else
	{
		printf("%-20s\t%-5s\t%-12s\t%-2s\t%-30s\n", "姓名", "性别", "电话", "年龄", "地址");
		printf("%-20s\t%-5s\t%-12s\t%-2d\t%s\n", pc->date[ret].name,
			pc->date[ret].sex, 
			pc->date[ret].tele,
			pc->date[ret].age,
			pc->date[ret].addr);
	}
}

3.5、修改联系人
利用查找函数找到联系人信息,再重新写入信息

void ModifyContact(struct contact* pc)
{
	char name[NAME_MAX];
	printf("请输入修改的姓名\n");
	scanf("%s", name);
	int ret = Find_name(pc, name);
	if (-1 == ret)
	{
		printf("此联系人不存在\n");
	}
	else
	{
		printf("请输入联系人的名字>:");
		scanf("%s", pc->date[ret].name);
		printf("请输入联系人的性别>:");
		scanf("%s", pc->date[ret].sex);
		printf("请输入联系人的电话>:");
		scanf("%s", pc->date[ret].tele);
		printf("请输入联系人的年龄>:");
		scanf("%d", &(pc->date[ret].age));
		printf("请输入联系人的地址>:");
		scanf("%s", pc->date[ret].addr);
		printf("成功修改\n");
	}
}

3.6、排序联系人
利用qsort函数进行排序

CmpByAge(const void* e1, const void* e2)
{
	return ((struct PeoInfo*)e1)->age - ((struct PeoInfo*)e2)->age;
}

void SortContact(struct contact* pc) 
{
	qsort(pc->date, pc->sz, sizeof(struct PeoInfo), CmpByAge);
}

总结

简单介绍了结构体,种类,功能,计算结构体大小,应用实例,对结构体有了更深的了解。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值