文章目录
前言
关于结构体(例如:内存对齐)、位段、枚举、联合等一些知识点的整理。
一、结构体
1.结构体内存对齐
(1)内存对齐规则
1、第一个成员在与结构体变量偏移量为0的地址处;
2、其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处(对齐数=编译器默认的一个对齐数 与 该成员大小的较小值)( vs中默认的值为8);
3、结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍;
4、如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构的对齐数)的整数倍。
(2)内存对齐计算
struct S1 {
char a;//(1,8)->1 //char的大小为1,vs默认对齐数为8,选择较小的1为对齐数
int b;//(4,8)->4//int的大小为4,vs默认对齐数为8,选择较小的4为对齐数
int c;//(4,8)->4//同上
};
如图所示:
嵌套了结构体的情况:
struct S2 {
int d;// (4,8)->4
struct S1 s;//(1,4,4)->4//由于Struct S1中的最大对齐数是4,所以s的对齐数是4
};
如图所示:
(3)修改默认对齐数
1.vs中默认的值为8;
2.使用 【#pragma pack(4) //修改默认对齐数为4】修改默认对齐数。
代码如下(示例):
struct S3 {
char a;//(1,8)->1
int b;//(4,8)->4
int c;//(4,8)->4
};
struct S4 {
int d;// (4,8)->4
struct S3 s3;//(1,4,4)->4
};
int main() {
struct S3 s3;
struct S4 s4;
printf("%d\n", sizeof(s3));//12
printf("%d\n", sizeof(s4));//16
printf("***************\n");
//offsetof - 宏
//计算结构体成员相对起始位置的偏移量的
printf("%u\n", offsetof(struct S3,a));//0
printf("%u\n", offsetof(struct S3,b));//4
printf("%u\n", offsetof(struct S3,c));//8
printf("***************\n");
printf("%u\n", offsetof(struct S4, d));//0
printf("%u\n", offsetof(struct S4, s3));//4
printf("***************\n");
//修改默认对齐数
//#pragma pack(4)//修改默认对齐数为4
printf("%d\n", sizeof(s3));//9
printf("%u\n", offsetof(struct S3, a));//0
printf("%u\n", offsetof(struct S3, b));//1
printf("%u\n", offsetof(struct S3, c));//5
return 0;
}
2.结构体内存不对齐
1、平台原因(移植原因);
2、性能原因:如果结构体内存不对齐进行存储,可能导致同一变量多次访问。
结构体的内存对齐是拿空间来换取时间的做法
3.结构体传参
结构体传参的时候,要传结构体的地址。
代码如下(示例):
struct S {
char name[20];
int age;
}s2;
int main() {
struct S s1;
scanf("%s %d", &s1.name, &s1.age);
scanf("%s %d", &s2.name, &s2.age);
printf("%s %d\n", s1.name, s1.age);
printf("%s %d\n", s2.name, s2.age);
return 0;
}
二、位段
1.格式
(1)位段的成员必须是int、unsigned int 或 signed int;
(2)位段的成员名后边有一个冒号和一个数字。
代码如下(示例):
struct S {
int _a : 2;//_a只占2bit
int _b : 5;//-b只占5bit
int _c : 10;//_c只占10bit
int _d : 30;//_d只占30bit
}s;//8B
2.内存分配
(1)内存分配计算
示例:
struct SS {
char a : 3;//a只占3bit
char b : 4;
char c : 5;
char d : 4;
}ss;
int main() {
printf("%d\n", sizeof(struct SS));//3;
ss.a = 10;//1010->010-> 010
ss.b = 12;//1100->1100-> 0110 0110
ss.c = 3;//11->00011-> 0000 0011
ss.d = 4;//100->0100-> 0000 0100
//&ss -> 0x 62 03 04
return 0;
}
如图所示:
三、枚举
枚举:把可能的取值一一列举,如季节,星期,性别等。
关键字:enum。
示例:
enum Season {
Spring,
Summer,
Autumn = 4,
Winter
};
int main() {
printf("%d\n", Spring);//0
printf("%d\n", Summer);//1
printf("%d\n", Autumn);//4
printf("%d\n", Winter);//5
return 0;
}
枚举中的每一个成员都是默认从0开始赋值的。
四、联合(共同体)
联合也是一种特殊的自定义类型,这种类型的变量也包括一系列的成员,特征是这些成员公用一块空间
。
1.计算
示例:
union Un {
char c;//1
int a;//4
};//由于最大的是4B,所以struct Un的大小至少是4
int main() {
union Un u;
printf("%d\n", sizeof(u));//4
printf("%p\n", &u);//由于共用一块空间,所以这三个地址是相同的
printf("%p\n", &u.c);
printf("%p\n", &u.a);
return 0;
}
2.应用
判断系统的大小端:
(1)给联合体中占用最大空间的a存入1;
(2)输出占用最小空间的c,其只占用1B;
(3)若是返回1系统就是小端,若返回0系统就是大端。
int check_Sys() {
//检查大小端
union Un {
char c;//1
int a;//4
}u1;//4
u1.a = 1;//00 01
return u1.c; //大端返回0 小端返回1
}
int main() {
if (check_Sys()) {
printf("小端\n");
}
else
{
printf("大端\n");
}
return 0;
}