1.结构体
结构体声明:
结构体是一些值的结合,这些值被称为成员变量,每个成员可以是不同类型的变量。
例如声明一个结构体"学生"
struct student
{
char name[20];
char sex[5];
int age;
float score[5];
};
也一些特殊情况下,也可以进行不完全的声明。
结构体变量的声明和初始化:
struct student
{
char name[20];
char sex[5];
int age;
float score[5];
}s1;//声明类型时同时定义变量s1
struct student s2;//定义变量s2
s2={"张三","男",18,{88.0,89.0,100.0,100.0,100.0}};//对变量初始化
struct student s3={"张三","男",18,{88.0,89.0,100.0,100.0,100.0}};//定义的同时对变量进行初始化。
结构体的自引用:
通过使用结构体指针可以进行结构的自引用
struct student
{
char name[20];
char sex[5];
int age;
float score[5];
struct student * s1;
};
结构体的内存对齐:
1.结构体的第一个成员放在偏移量为0的位置
(什么叫偏移量,就是在内存的地址与结构体起始地址之间的差值,偏移量为0意味着改成员的地址即为结构体的起始地址。)
2.其他成员对齐至对齐数整数倍的位置
(什么叫对齐数:对齐数是改成员的大小与默认对齐数的较小值,比如默认对齐数为8,该成员为int类型,大小是4个字节,那么对齐数就是4)
3.结构体的总大小为所有对齐数中的最大对齐数的整数倍。
4.若结构体中嵌套了其它结构体,那么被嵌套的这个结构体的对齐数就是它本身的最大对齐数,
整个结构体的大小是最大对齐数(包括被嵌套的结构体)的整数倍。
总的来说,结构体的内存对齐是拿空间换时间的做法。
若要做到即省空间又省时间,就需要把大小较小的结构体成员集中一起。
预处理指令#pragma 可以修改默认对齐数。
#pragma pack(8)//设置默认对齐数为8
#pragma pack()//取消设置的默认对齐数,还原为默认
结构体传参是传结构体指针会降低系统在压栈时的开销。
位段:
位段是通过结构体声明实现的,
struct S {
char a:3;//占用3个位的大小
char b:4;//占用4个位的大小
char c:5;//占用5个位的大小
char d:4;//占用4个位的大小
};
位段的跨平台问题
1. int 位段被当成有符号数还是无符号数是不确定的。
2. 位段中最大位的数目不能确定。(16位机器最大16,32位机器最大32,写成27,在16位机
器会出问题。
3. 位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。
4. 当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是
舍弃剩余的位还是利用,这是不确定的。
总结:
跟结构相比,位段可以达到同样的效果,但是可以很好的节省空间,但是有跨平台的问题存在。
枚举
枚举把可能存在的情况列举成数值,
enum Color//枚举类型
{
RED=1,//设定初识值为1,不设置则为0
GREEN=2,//自增1
BLUE=4
};
枚举常量的优点
1. 增加代码的可读性和可维护性
2. 和#define定义的标识符比较枚举有类型检查,更加严谨。
3. 防止了命名污染(封装)
4. 便于调试
5. 使用方便,一次可以定义多个常量
联合体(共用体)
联合体的声明类似与结构体的声明
union Un
{
char c;
int i;
};
与结构体不同的是
联合体的所用成员共用一个内存空间,所以联合体的大小至少是 最大成员的大小。
联合体大小的计算
联合的大小至少是最大成员的大小,联合体的大小是最大对齐数的整数倍。
例如
union Un1
{
char c[5];
int i;
};
最大成员的大小是5个字节,但最大对齐数是4,所以他的大小是8.
union Un2
{
short c[7];
int i;
};
最大成员的大小是14字节,最大对齐数是4,所以他的大小为16.