1.结构体
个人认为结构体和数组特别相似,只不过结构体和数组的区别在于结构体的成员可以是不同类型,而数组成员类型是相同的。
(1)结构体的声明
struct tag{成员列表//至少得有一个成员}值列表;//值列表可以省略struct{int a;char b;float c;}x;//匿名结构体struct{int a;char b;float c;}a[5],*p;//匿名结构体//注意上述的两个匿名结构体虽然成员列表看似相同,但编译器在进行编译的时候绝不会将这两个结构体看成是一样的结构体,所以对于*p = &x,这种看法是绝对错误的
(2)结构体成员
结构体成员可以是int,float,double,char等基本类型,也可以是数组,指针,甚至结构体,枚举等都可以。结构体成员在访问的时候采用结构结构体.成员名的方式来访问如:
#include<stdio.h>struct Student{char name[20];int age;int grade;};//学生类结构体int main(){struct Student student;.//定义一个学生结构体printf("%s\n", student.name);printf("%d\n", student.age);printf("%d\n", student.grade);return 0;}
由上可以看出,结构体的定义通过struct 结构体名来定义一个变量,但需要注意,单独的声明一个结构体类型,系统不为之开辟空间。
举个例子
struct Student{char name[20];int age;int grade;};//系统不为它开辟内存空间struct Student student;//系统为之开辟内存空间
(3)结构体成员的访问
结构体成员的访问采用.的方式访问,当有结构体指针的时候必须先对其解引用或者是结构体指针名 -> 成员名才可对其成员访问。
举个例子
#include<stdio.h>struct Student{char name[20];int age;int grade;};//学生类结构体int main(){struct Student student;.//定义一个学生结构体stuct Student* student1;printf("%s\n", student.name);printf("%d\n", student.age);printf("%d\n", student.grade);printf("%d\n", (*student1).grade);//解引用的方式printf("%d\n", student1 -> age);//箭头的方式return 0;}
(4)结构体的自引用
//方案一struct Student{char name[20];char sex[5];int age;struct Student* student;};//方案二typedef struct Student{char name[20];char sex[5];int age;struct Student* Next;}Student;//typedef定义一个新类型Student,以后在定义变量的时候可以直接用Student去定义一个结构体变量,而不用再用struct Student
(5)结构体的初始化
struct Student{char name[20];char sex[5];int age;int grade;};//方案一整体赋值struct Student student = {"张三", "男", 20, 90};//方案二逐个赋值struct Student student1;strcpy(student1.name, "李四");strcpy(student1.sex, "男");student1,age = 20;student1.grade = 89;
(6)结构体的内存对齐
结构体大小的计算
几条规则
1.结构体的第一个成员始终对齐于偏移量为0的地址(offsetof(结构体名, 成员名))处
2.后面的结构体成员对齐与某个数字(对齐数)的整数倍的地质处。其中对齐数在VS上默认是8,在Linux上默认是4。其中*对齐数*= 默认对齐数和自身成员大小的较小值
3.结构体的总大小为最大对齐数
4.结构体如果嵌套了结构体,嵌套的结构体对齐到自己的最大对齐数的整数倍的地址处。
举个例子
//VS下#include<stdio.h>int main(){struct S1{char c1;//1int i;//3 4char c2;//1};//12struct S2{char c1;//1char c2;//1int i;//2 4};//8struct S3{double d;//8char c;//1int i;//3 4};//16struct S4{char c1;//1struct S3 s3;//7 16double d;//8};//32printf("%d\n", sizeof(struct S1));//12printf("%d\n", sizeof(struct S2));//8printf("%d\n", sizeof(struct S3));//16printf("%d\n", sizeof(struct S4));//32return 0;}
不同平台对地址的访问要求不同,不是所有的硬件平台都可以访问任意地址的数据,有的硬件平台规定某些平台只能访问某些特定的数据,所以当访问不正确是会出现错误,而且如果没有内存对其,有的数据本来一次可以访问到,但却要两次访问,这样大大降低了内存的访问速度,因此便有了结构体的内存对其。
(7)结构体传参
结构体在传参时为了节省内存,在传参时采用地址传参,同时要对其成员进行访问时可以通过对其地址解引用的方式访问其成员
(8)结构体的位段
举个例子
struct A{int _a:2;int _b:5;int _c:10;int _d:30;};//位段类型printf("%d\n",sizeof(struct A));//不同平台结果不一样
A.位段的内存分配
a.位段成员只能是整型家族int,unsigned int , signed int,char
b.不跨平台
c.按一字节或四字节方式分配
B位段中的问题
a.将其当成有符号还是无符号不确定
b.成员内存从左分配还是从右分配不确定
c.当包含多个位段时,前一个位段所剩余内存应该舍弃还是保存都不确定
d.位段中最大位的数目不同机器不同
2.枚举
(1)枚举定义
将有限个可能取值都一一列举出来
enum Sex{MALE,FEMALE,SECRET};
(2)枚举的优点
A.增加代码可读性和可维护性
B.比起宏定义来,枚举对类型检查较为严谨
C.使用方便,一次可以定义多个变量
D.具有一定的封装代码作用
3.联合体
(1)联合特点
联合体公用一块内存空间
#include<stdio.h>int main(){union Un1{char c[5];//1int i;//4};//最大是5,但5不是最大对其数的倍数,所以是8union Un2{short c[7];//2 int i;//4};//最大是2 * 7 = 14, 但14不是最大对齐数4的倍数,所以变成了16printf("%d\n", sizeof(union Un2));return 0;}