文章目录
🚩前言
在前面已经提到了C语言中各种的内置数据类型,比如:
char\int\float\double\long long\long double
等,这些都是C语言自身支持的数据类型。但是今天所说的是自定义数据类型。通过这些构造数据类型可以用来描述一个具体的对象。
构造数据类型
:是一种可以包含多个不同的数据类型的数据组合,可以用来描述一个对象的属性。在C语言中构造类型有很多,比如:结构体、联合体(共用体)、枚举数据类型等。接下来就看看这些具体的构造数据类型------》👀👀
🥇结构体数据类型
我们从结构体定义开始道来:
🥈结构体的定义
- 在使用结构体的时,必须对结构体的组成进行熟悉,其形式如下:
c
struct 结构体名
{
结构体成员;
};
- 结构体成员的定义和简单变量的定义形式一样。
1、比如我定义一个描述人的结构体
c
//结构体定义
struct Person
{
char name[20];//姓名
int age;//年龄
char sex;//性别
long indentityNo;//身份证号
char addr[40];//地址
};
在上例子中,
struct
为关键字,Person
为结构体名,大括号里面的就是成员。在结构体类型中具有一些特点:
(1)结构体名为任何合法的标识符,建议命名的时候做到见名知意。
(2)虽然成员的定义和简单变量定义一样,但是不能直接使用。
🥈结构型变量的定义
一旦创建好结构体,就可以定义结构体变量了。我们可以采用3种方式定义结构体变量:
1、先定义结构体,再定义结构体变量
定义格式:类型标识符 <变量名列表>;
strcut Person
stu,worker;
通过创建的
Person
结构体,创建了两个变量stu
和worker
,两个都是结构体struct Person
的变量。'struct Person'代表类型名,即表示它是结构体类型。如同int
定义两个变量a
和b
(int a,b
),int
就是类型标识符,表示a
和b
是整型。格式不要错写成:
struct stu,worker;
(没有声明是哪一种的结构体类型)
Person stu,worker;
(没有关键字struct
,就不认为是结构体)。这两种都是错误的格式。
在定义好结构体类型后就可以定义不同变量了:
struct Person teacher,doctor;
struct Person *student;
2、在定义结构体的同时定义一个或者多个结构体类型变量
定义格式:
c
struct <结构体名>
{
成员列表;
}<变量名1>......;
c
struct Person
{
long no;
char name[20];
int age;
char sex;
long indentityNo;
char addr[40];
}student,doctor,worker;
struct Person stu;//在外面有定义变量
在此定义的基础上还可以在外面定义结构体变量。
3、直接定义结构体变量名------《匿名结构体》
定义格式:
c
struct
{
成员列表;
}<变量名>;
c
struct
{
long no;
char name[20];
int age;
char sex;
long indentityNo;
char addr[40];
}student,doctor,worker;
//struct stu;//不合法,因为没有定义结构体名
匿名结构体定义方法:就是只定义了3个变量
student,doctor,worker
,但没有定义结构体名,因此就不能再定义其他变量了。
当然,在定义变量的时候,既可以是全局变量,也可以是局部变量。
🥈结构体内存对齐
在清楚结构体怎么创建后,来看看结构体的内存对齐问题,也就是讨论结构体在内存中的字节大小。我们先通过例子来打开内存对齐的知识点:
c
//结构体内存对齐
#include<stdio.h>
struct S
{
char c1;//1Byte
int num;//4Byte
char c2;//1Byte
}s;
int main()
{
printf("%zd\n",sizeof(s));//6Byte?为什么输出12Byte
return 0;
}
为什么是12Byte?
内存对齐规则:
- 结构体的第⼀个成员对⻬到和结构体变量起始位置偏移量为0的地址处。也就是第一个成员必须是结构体首地址处。
- 其他成员变量要对⻬到某个数字(对⻬数)的整数倍的地址处。
对⻬数 = 编译器默认的⼀个对⻬数 与 该成员变量⼤⼩的较⼩值。
- VS 中默认的值为 8
- Linux中 gcc 没有默认对⻬数,对⻬数就是成员⾃⾝的⼤⼩
- 结构体总⼤⼩为最⼤对⻬数(结构体中每个成员变量都有⼀个对⻬数,所有对⻬数中最⼤的)的整数倍。
- 如果嵌套了结构体的情况,嵌套的结构体成员对⻬到⾃⼰的成员中最⼤对⻬数的整数倍处,结构体的整体⼤⼩就是所有最⼤对⻬数(含嵌套结构体中成员的对⻬数)的整数倍。
图解为什么是12Byte?
我们看到确实是12Byte,但是浪费了6Byte,接下来换一个顺序来看一看:
c
//2、结构体内存对齐
#include<stdio.h>
struct S
{
char c1;//1Byte
char c2;//1Byte
int num;//4Byte
}s;
int main()
{
printf("%zd\n",sizeof(s));
return 0;
}
- 结果展示:
图解:
注意:内存对齐,不能理解为,每个成员所占空间要和成员中占字节最大的成员一样。也就是两个char类型成员和一个int 类型成员,在算总字节时候不能把char类型的字节算作是4Byte,如果这样算总字节就是3*4=12,这样是错误的理解。必须根据偏移量来算。
🥈结构型变量的初始化
在了解了结构体的创建后,接下来就得给成员变量初始化了,结构体成员初始化,和其他简单变量初始化一样,结构体变量也可以在变量定义的时候进行初始化。比如下面代码中就是在定义变量的同时加以初始化:
c
//3、结构体成员初始化
#include<stdio.h>
struct Person
{
char name[15];//不能直接在成员变量后面初始化,char name[15]="张三";
//因为结构体规定结构体成员变量不能直接使用。
int age;
char sex;
char addr[40];
}student={"轩辕",25,'男',"重庆"};
如果结构体嵌套结构体,初始化的时候需要对各个成员初始化:
c
//4、结构体嵌套
#include<stdio.h>
struct Data
{
int day;
int month;
int year;
};
struct Person
{
char name[15];
struct Data birthday;
int age;
char sex;
char addr[40];
}student={"张三",23,5,2014,10,'男',"重庆"};
当我们定义的是结构体指针变量的时候,不能直接初始化指针变量。就像下面这种写法是错误的:
struct Person *worker={"王岑",26,"软件工程","上海"};
//错误的初始化。
struct Person *worker = &((struct Person){"王岑", 26, "软件工程", "上海"});
//结构体指针变量正确初始化
也可以在外面初始化
c
#include<stdio.h>
struct Person
{
char name[15];
int age;
char sex;
char addr[40];
};
struct Person student1={"轩辕",25,'男',"重庆"};//全局变量
int main()
{
struct Person student1={"轩辕",25,'男',"重庆"};//局部变量
return 0;
}
🥈结构型变量成员的引用
结构体创建好了,成员变量也初始化好了。接下来我们来看看该怎么访问初始化好的成员呢?
🥉引用结构体类型变量中的成员
格式:
<结构体变量名>.<成员名>
或者 <(*结构体指针变量名)>.<成员名>
或者<结构体指针变量名> -><成员名>
c
//结构体解引用
#include<stdio.h>
//创建结构体
struct Person
{
char name[15];
int age;
char profession[30];
char addr[50];
};
int main()
{
//创建结构体变量
struct Person student1={"张三",21,"计算机","重庆"};
struct Person *worker=&(struct Person){"王岑",26,"软件工程","上海"};
//此处不能写为struct Person *worker={"王岑",26,"软件工程","上海"};
//因为是指针变量不能直接初始化。
struct Person *teacher=&((struct Person){"文宇",29,"计算机老师","成都"});
printf("姓名\t年龄\t专业\t\t地址\n");
//解引用操作来访问
printf("%s\t%d\t%s\t\t%s\n",student1.name,student1.age,
student1.profession,student1.addr);
printf("%s\t%d\t%s\t%s\n",(*worker).name,(*worker).age,
(*worker).profession,(*worker).addr);
printf("%s\t%d\t%s\t%s\n",teacher->name,teacher->age,
teacher->profession,teacher->addr);
return 0;
}
结果展示:
🥉将结构体变量作为一个整体来使用
可以将一个结构体变量作为一个整体赋值给另一个结构体变量,前提是两个结构体变量必须具有相同的结构体类型。
比如:
struct Person doctor={"Li_ming",25,"高数老师","贵州"};
struct Person teacher=doctor;
teacher=doctor;
//赋值以后
teacher
中的信息和doctor
中的信息相同的。
- 以下是两个结构体------课程成绩和学生信息。看结构体成员的使用。
c
#include<stdio.h>
//学生成绩信息结构体
struct score
{
int math;
int english;
int computer;
};
//学生基本信息结构体
struct stu
{
char name[15];
char sex;
long stuNo;
struct score sub;
};
int main()
{
struct stu student1={"张三",'M',20230001,85,76,95};
struct stu student2;
student2=student1;
printf("姓名\t性别\t学号\t\t高数\t英语\t计算机\n");
printf("%s\t%c\t%d\t%d\t%d\t%d\n",
student1.name,student1.sex, student1.stuNo,student1.sub.math,
student1.sub.english,student1.sub.computer);
printf("%s\t%c\t%d\t%d\t%d\t%d\n",
student2.name,student2.sex, student2.stuNo,student2.sub.math,
student2.sub.english,student2.sub.computer);
return 0;
}
结果展示:可以看到student1的信息给到了student2,打印出来是一样的。
🥇结构体数组
简单来说,一个结构体变量只能存放一个对象的数据,上述代码中就是创建
student1
、student2
便是两个学生信息,但是要存储50个学生呢?难道创建50个学生对象吗?不会觉得很难写吗?理论上是可以的,只是很麻烦。在这就会用到结构体数组
,即数组中的每个元素都是结构体类型变量。我们来看看吧。
🥈结构体数组的定义
可以用以下3种方式定义:
1、先定义结构体,再定义结构体数组
c
struct <结构体名>
{
成员列表;
};
struct <结构体名> <数组名>[数组大小];
2、在定义结构体的同时定义结构体数组
c
struct <结构体名>
{
成员列表;
}<数组名>[数组大小];
3、匿名结构体定义结构体数组
c
struct
{
成员列表;
}<数组名>[数组大小];
🥈结构体数组成员的初始化和引用
直接用代码演示:
c
//7、结构体数组初始化
#include<stdio.h>
struct Person
{
char name[10];
int age;
char sex;
char addr[20];
};
int main()
{
struct Person student[30]={{"hang Ming",23,'F',"潮汕"},
{"Zhao Chen",25,'M',"成都"},
{"Zhou Ting",26,'F',"昆明"}};
//student[3]~student[29]是不确定的。
printf("姓名\t\t年龄\t性别\t地址\n");
for(int i=0;i<3;i++)
{
printf("%2s\t%2d\t%2c\t%2s\n",student[i].name,student[i].age,
student[i].sex,student[i].addr);
}
return 0;
}
结果展示:
🥇结构体类型变量与函数
利用函数来对结构体变量进行操作:
🥈函数的形参和实参为结构体
- 设计一个函数来打印处购书的信息:代码如下------》
c
//8、结构体变量与函数
#include<stdio.h>
//购书信息结构体
struct BookList
{
char name[20];
int number;
float price;
float SumMoney;
};
//输出函数
void print_book_list(struct BookList book)//形参接收
{
book.SumMoney=book.number*book.price;
printf("%-24s%d\t%.2f\t%.2f\n",book.name,book.number,
book.price,book.SumMoney);
}
int main()
{
//购置的书数量不止一本,所以用结构体数组
struct BookList Book[4];
float Total=0;
printf("请输入这4本书的信息:书名 数量 单价\n");
for(int i=0;i<4;i++)
{
scanf("%s%d%f",&Book[i].name,&Book[i].number,&Book[i].price);
Total+=Book[i].number*Book[i].price;
}
printf("--------------------------------------------\n");
printf("购置书清单:\n");
printf("书名\t\t\t数量\t单价\t合计\n");
for(int i=0;i<4;i++)
{
print_book_list(Book[i]);//传过去实参
}
printf("购书金额总计:%.2f\n",Total);
return 0;
}
结果展示:
🥈函数返回值为结构体类型
我们知道
int,float,double,void,各种数据的指针类型
都可以作为返回值。在新的C语言标准中函数返回值可以是结构体相关的类型:代码如下:
c
//1、结构体数组作为函数返回值的使用
#include<stdio.h>
//创建购置书的信息结构体
struct BookList
{
char name[30];
int number;
float price;//单价
float SumMoney;//总计
};
//两个函数
//1、struct BookList* InputBookInfo(struct BookList *book);//返回的是结构体指针类型
//2、void Print_Book_List();//打印函数
float Total=0;//全局变量存储总计金额
//用结构体指针类型作为返回值,传入的是地址,参数用指针
struct BooKList* InputBookInfo( struct BookList *book)
{
scanf("%s%d%f",&book->name,&book->number,&book->price);
Total+=book->number*book->price;
return book;
}
void Print_Book_List(struct BookList book)
{
book.SumMoney=book.number*book.price;
printf("%-24s%d\t%.2f\t%.2f\n",book.name,book.number,book.price,book.SumMoney);
}
int main()
{
//创建结构体数组变量
struct BookList Book[4];
//输入信息
printf("请输入购置书的信息:书名 数量 单价\n");
for(int i=0;i<4;i++)
{
InputBookInfo(&(Book[i]));//调用购置书的信息输入函数
}
printf("----------------------------------------\n");
printf("购书清单:\n");
printf("书名\t\t\t数量\t单价\t合计\n");
for(int i=0;i<4;i++)
{
Print_Book_List(Book[i]);
}
printf("购书金额总计:%.2f\n",Total);
}
结果展示:
🥇共用体数据类型
共用体,也被称为联合体。它是把不类型的数据项组合成一体的,这些数据项在内存单元中所占用的起始单元是相同的。
它的定义和结构体一样,只是关键字不一样:👀👀
🥈共用体的定义
1、先定义共用体,再定义共用体类型
c
union <共用体名>
{
成员列表;
};
union <共用体名> <变量名>;
2、在定义共用体的同时定义共用体变量
c
union <共用体名>
{
成员列表;
}<变量名1>......;//可以多个变量名
3、匿名共用体
c
union
{
成员列表;
}<变量名1>......;//可以多个,但是不能再定义另外的共用体变量。
🥈共用体与结构体的内存存储对比
c
//2、共用体和结构体内存
#include<stdio.h>
//创建结构体
struct memb
{
float f;
int i;
char c;
};
//创建共用体
union Un
{
float f1;
int n;
char c;
};
int main()
{
//创建变量
struct memb st={0};
union Un un={0};
//求所占字节大小
printf("结构体:%zd\n",sizeof(st));//12Byte
printf("共用体:%zd\n",sizeof(un));//4Byte
return 0;
}
结果展示:
🥈共用体的初始化与解引用
共用体类型的变量的引用方式和结构体变量引用方式类似:
c
<共用体名>.<成员名>;
- 通过代码了解共用体成员初始化:
c
//3、共用体成员的初始化
#include<stdio.h>
//创建共用体
union Un
{
double de;
int num;
char c;
};
int main()
{
//创建共用体变量
union Un tag;
tag.de=6.66;
tag.num=20;
tag.c='p';
printf("共用体成员的值:\n");
printf("tag.de=%lf\ntag.num=%d\ntag.c=%c\n",tag.de,tag.num,tag.c);
return 0;
}
结果展示:和赋给的值有点差别
🥇枚举数据类型
枚举类型,是用标识符表示的整型常量的集合,从其作用上看,枚举型常量是自动设置值的符号常量。定义形式如下:
枚举型常量的起始值为
0
;
c
enum <枚举类型名>
{
标识符1,
标识符2,
......
};
代码展示:
c
//4、枚举的使用
#include<stdio.h>
//创建枚举
enum Months
{
//January,
January=1,
February,
March,
April,
May,
June,
July,
August,
September,
October,
November,
December
};
int main()
{
//只有在定义枚举类型后,在可以创建枚举变量
enum Months month;//创建枚举变量
for(month=January;month<=December;month++)
{
printf("%2d ",month);
}
return 0;
}
第一个结果是January赋值为1的结果,默认是从起始位置0开始:
第二个是把January赋值为1的,就从1开始。
等待下一节知识吧👀