提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
结构体、枚举、联合体
结构体
结构
结构就是一些值的集合,每个值都称为成员变量。每个成员都可以是不同的变量。
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);
}
总结
简单介绍了结构体,种类,功能,计算结构体大小,应用实例,对结构体有了更深的了解。