一、结构体
1、结构的声明
struct tag//结构体类型名
{
member-list;//结构体成员
}variable-list;//结构体变量
例如写一个描述学生的结构体
struct Student
{
char name[10];//姓名
int age;//年龄
char ID[10];//学号
};//注意这里的分号不能丢
还有一些特殊的声明方式---匿名结构体,不给tag,如下
struct
{
int a;
char b;
}x1;
上诉匿名结构体只能在声明之后立即声明变量,不然无法使用
下面,我们来看看下面的代码是否正确
struct
{
int a;
char b;
}x1;//创建一个变量
struct
{
int a;
char b;
}*p;//创建了一个结构体指针
int main()
{
p=&x1;
return 0;
}
这样做是不被允许的,编译器会认为这是两个独立的结构体,类型不匹配,无法赋值
2、结构体的自引用(自己引用自己)
数据结构的链表就是结构体的自引用实现的
struct Node
{
int data;
struct Node*next;
};
那么我们可不可以这样写?
typedef struct Node
{
int data;
Node*next;
}Node;
编辑器从上到下依次执行代码,struct Node还没有被重命名之前就已经被使用,故是不行的
3、结构体变量的定义和初始化
首先我们要明白一点,结构体是一种类型,和int,char一样,那么变量的定义也是同样的道理
struct Stu
{
char name[10];
int age;
int num;
}s1;//全局变量----除了这种特殊的方式,其他的都和int、char什么的一样
struct Stu s2;//全局变量
int main()
{
struct Stu s3;//局部变量
return 0;
};
那么有了变量我们应该如何初始化呢
struct Stu
{
char name[10];
int age;
int num;
}s1;
int main()
{
struct Stu s1={"zhangsan",40,1};//该方法顺序要与结构体成员的顺序相同
struct Stu s2={.age=20,.num=2,.name="lisi"};//该方法可以任意顺序
return 0;
}
4、结构体内存对齐
我们都知道,基本数据类型都是有大小的,比如int是4字节,char是1字节,float是4字节等等,那么结构体类型的大小是几个字节呢?
下面举个例子来说明一下---结构体是如何计算大小的
struct S
{
char a;
int b;
char c;
};
int main()
{
printf("%d",sizeof(struct S));
return 0;
}
很多人会认为该结构体的大小就是1个int的大小加上2个char的大小共6个字节,那就太想当然了
要想计算结构体大小,首先我们要了解结构体的对齐规则:
- 第一个成员在与结构体变量偏移量为0的地址处。
- 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。(VS中默认的值为8)
- 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
- 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍
了解完规则,我们就可以试着来解释一下上诉代码结果
注意:默认对齐数是可以修改的
#pragma pack(1)//默认对齐数设置为1
struct S
{
char a;
int b;
char c;
};
#pragma pack()//恢复默认对齐数
int main()
{
printf("%d", sizeof(struct S));
return 0;
}
那么,结构体为什么要对齐
1.考虑到移植性,不是每种硬件设备都能访问任意地址的
2.考虑到性能,处理器可以更快的读取数据
节省空间的小技巧:可以将较小的类型放在一起,比如刚刚的结构体可以写成
struct S
{
char a;
char c;
int b;
};//大小是8字节
提示:结构体的传参尽量传地址,防止结构体太大,占用的空间太多
二、位段
相信很多人都只知道段位,而没有听过位段
其实位段和结构体很相似,有两个不同:
1.位段的成员必须是 int、unsigned int 或signed int 。
2.位段的成员名后边有一个冒号和一个数字。
下面我写一个位段,给你们看看
struct S
{
int _a:1;
int _b:3;
int _c:10;
int _d:30;
};
数字代表该成员变量占有几个bit位(1个字节是8个bit位)
由于位段的一些内存方面的标准,C语言标准也没有明确的指示,故不同的编译器有不同的分配内存方法,这里就不细讲了,没什么普适性。
三、枚举类型
enum Sex
{
MALE,//默认为0
FEMALE,//1
SERCRET//2
};
enum Week
{
MON,//0
TES,//1
WES,//2
THUR=5,//5
FRI,//6
SAT,//7
SUN//8
};
int main()
{
enum Sex s = SERCRET;
printf("%d\n",MALE);//0
printf("%d\n",FEMALE);//1
printf("%d\n",MON);//0
printf("%d\n",THUR);//5
printf("%d\n",SUN);//8
return 0;
}
注意:枚举类型的大小是4个字节
相对于#define枚举的优点:
- 增加代码的可读性和可维护性
- 枚举是一种类型,更加的严谨
- enum更加方便,一次可以定义多个常量
- 防止了命名污染
- 方便调试