方便编程的构造数据类型叫做数组。数组里的元素必须是同意类型的,但是要是类型不同又要怎么处理呢?C语言里面又有另一个构造数据类型——结构体。和他相似的还有枚举、联合。
目录
1、>结构体类型创建
2、>结构体初始化
3、>结构体内存对齐
4、>位段,位段计算机大小。
5、>枚举+联合。
6、>实现一个通讯录;
结构体的关键字struct
struct 结构体名字 { //这里是结构体类型定义
类型+成员变量1;
类型+成员变量2;
……
}变量名字; //这个就是一个结构体,当然也可以没有这个名字,就会有一个匿名的结构体
下面举个栗子
//C语言
//结构体的定义
struct STUDENT{//这里struct STUDENT是类型名
char name[1024];
char sex[1024];
int age;
} student;//变量名,就是一个结构体的变量。如果用“ , ”隔开。就都是这个就结构体类型的变量了
//类似,int a,b,c,d。
在后面如果还需要定义一个相同结构体变量的话,必须带上整个结构体类型名
如:再定义一个结构体
//C语言
struct STUDENT student2;
是不是感觉非常麻烦?每次要写很长很长。所以这里又要运用以前的一个关键字——typedef。在定义的时候呢,我们就可以直接改变它的名字。那么后面定义的时候就不同这么麻烦。
//C语言
typedef struct STUDENT{//这里struct STUDENT是类型名
char name[1024];
char sex[1024];
int age;
}STUDENT;//最后这里就是类型名了,注意这里不是一个结构体的变量哦。
现在会不会有人问可不可以不要结构名?是可以不要,但不推荐。如果代码里结构体很多,就不知道定义的是哪个结构体。到最后会很懵。
既然是定义的变量,那么就需要进行初始化。但是结构体因为是很多类型的一个集合,所以初始化的时候和其他类型数据是不同的,下面举个栗子:
//C语言
struct STUDENT{
char name[1024];
char sex[1024];
int age;
} student1,student2={"zhangsan","nan",12};
student1 = student2;
初始化的方式就是,用 { } 括起来,然后根据结构体里面是啥类型就用该类型的初始化方式进行初始化。
这个内容非常关键。这个内容就联系到一个结构体占多大的内存了。也可以让我们认识到计算机CPU的处理数据的机制。
第一次见内存对齐很陌生。但其实并不难理解。下面举个栗子。
//C语言
struct MyStruct
{
char ch1;
int num;
char ch2;
}test;
printf("%d\n",sizeof(test));
明明是两个char和一个int 应该是6才对啊。为什么会是12 呢?
这就要说到CPU处理数据的方式了。刚才提到过。CPU取数据的时候都是以4或8个字节开始取。所以如果四个字节一取,那么上面例子中的int 是不是只取了 3 / 4 字节 啊?CPU 就需要再取一次int。 所以为了提高效率就出现了内存对齐。
》》应该怎么对齐?
对齐数 = 编译器默认对齐数(VS=8)与该成员大小的较小值。
上面的都是官方语言;下面来解释解释
用这个栗子
//C语言
struct MyStruct
{
char ch1;
char ch2;
int num;
}test;
printf("%d\n",sizeof(test));//==8
地址
0 ch1 在第一个内存地址上, 1<8
1 ch2 因为对齐数是 1.所以在这。记得对齐数算法吧1<8
2
3
4 num 这里对齐数是 4 。 要在4的倍数的位置
5 num
6 num
7 num 所以大小是 8
栗子清晰吧?
另外:编译器默认的对齐数是可以修改的哈。这样加上预处理指令,就OK了
#pragma pack(对齐数)
官方:位段,C语言允许在一个结构体中以位为单位来指定其成员所占内存长度利用位段能够用较少的位数存储数据。
位段成员必须声明为int,signed int或unsigned int,在成员名的后面是一个冒号和一个整数
这个整数代表这占多少位
栗子:
struct MyStruct
{
int a : 2;
int b : 4;
int c : 7;
int d : 30;//4
}bit;
printf("%d\n",sizeof(bit));
结果是多少呢?前面结构体的内存对齐哦
信息的存取一般以字节为单位。实际上,有时存储一个信息不必用一个或多个字节。所以就用了位段来节省空间啦
枚举——enum
枚举其实和定义宏差不多,但是枚举用起来方便。枚举目前用的少。所以简单介绍一下
枚举是指将变量的值一一列举出来,变量只限于列举出来的值的范围内取值。
枚举型是一个集合,集合中的元素(枚举成员)是一些命名的整型常量,元素之间用逗号,隔开。
第一个枚举成员的默认值为整型的0,后续枚举成员的值在前一个成员上加1。
enum MyEnum
{
Mon,
Tue,
Wed,
Thur,
Fri
};
printf("%d %d %d %d %d\n",Mon, Tue,Wed,Thur,Fri);
你把第一个成员设置成什么数字,后面会依次加一
enum MyEnum
{
Mon,
Tue,
Wed=5,
Thur,
Fri
};
printf("%d %d %d %d %d\n",Mon, Tue,Wed,Thur,Fri);
如果设置中间的变量,那么从该变量之后在该变量上依次加一。例如上面Wed=5,那么Thur=6,Fri=7
联合体
联合也是一种特殊的自定义类型 这种类型定义的变量也包含一系列的成员,特征是这些成员公用同一块空间(所以
联合也叫共用体)
特点:
联合变量的大小,至少是最大成员的大小(因为联合至少得有能力保存最大的那个成员)
当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍
//C语言
//联合类型的声明
union Un
{
char c;
int i;
};
//联合变量的定义
union Un un;
//计算连个变量的大小
printf("%d\n", sizeof(un))
下面来个结构体的例子,作为总结。
//C语言
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
//实现一个通讯录;
//通讯录可以用来存储1000个人的信息,每个人的信息包括:
//姓名、性别、年龄、电话、住址
//
//提供方法:
//1. 添加联系人信息
//2. 删除指定联系人信息
//3. 查找指定联系人信息
//4. 修改指定联系人信息
//5. 显示所有联系人信息
//6. 清空所有联系人
//7. 以名字排序所有联系人
//定义一个结构体数组,每个数组的元素里面包含一个联系人信息
#define PERSONMAX 1000
struct BOOK{
char name[1024];
char sex[1024];
int age;
char tel[1024];
char address[1024];
int personcount;
}address_book[PERSONMAX];
int book_menu() {
int choice = 0;
printf("=========<简易通讯录>=========\n");
printf("=========<1、添加联系人>=========\n");
printf("=========<2、删除联系人>=========\n");
printf("=========<3、查找联系人>=========\n");
printf("=========<4、修改联系人>=========\n");
printf("=========<5、显示全部>==========\n");
printf("=========<6、清空联系人>=========\n");
printf("=========<7、排序联系人>=========\n");
printf("=========<8、退出通讯录>=========\n");
printf("请输入您的选择:");
scanf("%d", &choice);
return choice;
}
void empty_person() {//清空和初始化的相同作用。所以这里可以写成初始化
address_book->personcount = 0;
memset(address_book, 0x0, sizeof(address_book));
printf("通讯录已经初始化!\n");
}
void add_person() {
if (address_book->personcount==PERSONMAX) {
printf("通讯录已满,添加失败!\n");
}
else {
printf("请输入姓名:");
scanf("%s", address_book[address_book->personcount].name);
printf("请输入性别:");
scanf("%s", address_book[address_book->personcount].sex);
printf("请输入年龄:");
scanf("%d", &address_book[address_book->personcount].age);
printf("请输入电话:");
scanf("%s", (address_book[address_book->personcount]).tel);
printf("请输入地址:");
scanf("%s", address_book[address_book->personcount].address);
}
printf("成功增加!\n");
++address_book->personcount;
printf("目前%d位联系人!\n", address_book->personcount);
}
int find_person() {
char name[1024] = "0";
printf("请输入查找人的姓名:");
scanf("%s",name);
int temp = 0;
printf("编号\t姓名\t性别\t年龄\t电话\t住址\n");
printf("=================================================\n");
while (temppersoncount){
if (strcmp(name,address_book[temp].name)==0) {
printf("%d\t%s\t%s\t%d\t%s\t%s\t", address_book[temp].personcount, address_book[temp].name, address_book[temp].sex
, address_book[temp].age, address_book[temp].tel, address_book[temp].address);
printf("\n");
return temp;
}
++temp;
}
printf("通讯录里没有该联系人!\n");
return -1;
}
void del_person() {
int temp = 0;
temp = find_person();
if (temp==0) {
;
}
else {
while (temppersoncount){
address_book[temp] = address_book[temp + 1];
++temp;
}
--address_book->personcount;
printf("成功删除该联系人!\n");
}
}
void alt_person() {
int temp = 0;
temp = find_person();
if (temp==-1) {
;
}
else {
int choice = 0;
int max = 5;
while (max--) {//有五次修改机会,没选择一次就会减少一次。
printf(" ======1、姓名 === 2、性别=======\n");
printf(" ======3、年龄 === 4、电话=======\n");
printf(" ======5、地址 === 6、跳过=======\n");
printf("请选择要修改的项:");
scanf("%d", &choice);
switch (choice)
{
case 1:
printf("姓名修改为:");
scanf("%s", address_book[temp].name);
break;
case 2:
printf("性别修改为:");
scanf("%s", address_book[temp].sex);
break;
case 3:
printf("年龄修改为:");
scanf("%d", &address_book[temp].age);
break;
case 4:
printf("电话修改为:");
scanf("%s", address_book[temp].tel);
break;
case 5:
printf("地址修改为:");
scanf("%s", address_book[temp].address);
break;
case 6:
printf("请继续选择。\n");
break;
default:
++max;//输入错误,加一次修改机会。
break;
}
}
}
}
void print_person() {
if (address_book->personcount==0) {
printf("通讯录为空,请先添加联系人!\n");
}
else {
int temp = 0;
printf("编号\t姓名\t性别\t年龄\t电话\t住址\n");
printf("=================================================\n");
while (temppersoncount)
{
printf("%d\t%s\t%s\t%d\t%s\t%s\t", address_book[temp].personcount, address_book[temp].name, address_book[temp].sex
, address_book[temp].age, address_book[temp].tel, address_book[temp].address);
printf("\n");
++temp;
}
}
}
void sort_person() {
if (address_book->personcount == 0)
{
printf("通讯录为空,不需要排序!\n");
}
else
{
int row = 0;
while (row < address_book->personcount - 1)
{
int col = 0;
while (col < address_book->personcount - 1 - row)
{
if (strcmp(address_book[col].name,address_book[col + 1].name) > 0)
{
char temp[1024];
strcpy(temp, address_book[col].name);
strcpy(address_book[col].name, address_book[col + 1].name);
strcpy(address_book[col + 1].name, temp);
}
++col;
}
++row;
}
}
}
int main(){
int choice = 0;
//以循环打印菜单提示用户输入选择
empty_person();
while (1) {
//开关语句卡是实现函数,增删查改,排序,清空。
choice=book_menu();
switch (choice){
case 1://添加联系人
add_person();
break;
case 2://删除联系人
del_person();
break;
case 3://查找联系人
find_person();
break;
case 4://修改联系人
alt_person();
break;
case 5://显示所有联系人
print_person();
break;
case 6://清空联系人
empty_person();
break;
case 7://排序联系人
sort_person();
break;
case 8:
printf("欢迎下次使用!\n");
system("pause");
return 0;
break;
default:
break;
}
}
return 0;
system("pause");
}