结构体的对齐规则
1. 第一个成员在与结构体变量偏移量为0的地址处。
2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
对齐数 = 编译器默认的一个对齐数(一般为4或者8) 与 该成员大小之间的较小值。
3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
例题1
1.在32位系统环境,编译选项为4字节对齐。那么sizeof(A)和sizeof(B)分别为多少?(sizeof求所占空间的大小,单位字节)
#pragma pack(4)//设置默认对齐数为4
struct A
{
int a;
short b;
int c;
char d;
};
struct B
{
int a;
short b;
char c;
int d;
};
#pragma pack()//取消默认设置的对齐数
int main()
{
struct A a = { 0 };
struct B b = { 0 };
printf("%d\n", sizeof(a));
printf("%d\n", sizeof(b));
}
struct A最大对齐数,及成员的对齐数:
int a 大小为4个字节 == 默认对齐数为4 所以 对齐数为4
short b 大小2个字节 < 默认对齐数 对齐数为2
int c 大小4个字节 == 默认对齐数 对齐数为4
char d 大小1个字节 < 默认对齐数 对齐数为1
故最大对齐数:4
第一个成员从0开始,a位置0-3,b的对齐数为2,此处为偏移量为4的地址处,满足是2的倍数,故b填入位置4-5,c的对齐数为4,6-7偏移处地址不满足为4的整数倍被跳过而被浪费,c填充在8-11位置,d对齐数为1填充在位移为12处。从0偏移到12偏移共13字节,而结构体大小为最大对齐数的整数倍,故结构体大小为16字节。
同理struct B最大对齐数,及成员的对齐数:
int a 大小为4个字节 == 默认对齐数为4 所以 对齐数为4
short b 大小2个字节 < 默认对齐数 对齐数为2
char c 大小1个字节 < 默认对齐数 对齐数为1
int d 大小4个字节 == 默认对齐数 对齐数为4
故最大对齐数:4
第一个成员从0开始,a位置0-3,b的对齐数为2,此处为偏移量为4的地址处,满足是2的倍数,故b填入位置4-5,c的对齐数为1,此处偏移量为6是1的倍数,d对齐数为4,7偏移处地址不满足为4的整数倍被跳过而被浪费,d填充在位移为8-11处。从0偏移到11偏移共12字节,而结构体大小为最大对齐数的整数倍,故结构体大小为12字节。
例题2:下面输出结果为?
typedef struct{//typedef对类型重定义
int a;
char b;
short c;
short d;
}AA_t;//AA_t为重定义的结构体类型
int main()
{
printf("%d\n",sizeof(AA_t));
return 0;
}
int a 占0-3偏移,char b 4偏移处
5偏移处浪费掉
short c 6-7偏移,short d 8-9偏移
总共占了10个字节 0-9偏移
由于最大对齐数为4,结构体大小为最大对齐数的整数倍,故结构体大小sizeof(AA_t)=12
包含位段成员的结构体大小
例题1:计算下题pointer分配的空间大小?
#define MAX_SIZE 2+3
struct Test
{
unsigned char a : 4;
unsigned char b : 2;
unsigned char c;
unsigned char d : 1;
}*Record;
int main()
{
struct Test* pointer = (struct Test*)malloc(sizeof(struct Test) *MAX_SIZE);
//pointer分配的空间
printf("%d", sizeof(struct Test) *MAX_SIZE);
}
本题需要主要考察包含了位段成员的结构体大小计算及对宏的了解程度
位段成员包含char是按1个字节开辟内存的,第一个成员a开辟一个字节等于8个bit位,被a用去4个bit,还剩4个bit可以给b用去2个bit,故位段a,b共用一个字节
c不是位段重新开辟一个字节的内存,它直接占一个字节
位段d只占1个bit,但也要开辟1个字节的空间,故一起共开辟了3个字节的空间
sizeof(struct Test)*MAX_SIZE=3*2+3=9,pointer分配的空间为9个字节
例题2.下题输出结果是?
int main()
{
unsigned char puc[4];
struct tagPIM
{
unsigned char a;
unsigned char b : 1;
unsigned char c : 2;
unsigned char d : 3;
}*p;
p = (struct tagPIM*)puc;
memset(puc, 0, 4);//memset:内存函数
p->a = 2;
p->b = 3;
p->c = 4;
p->d = 5;
printf("%02x %02x %02x %02x \n", puc[0], puc[1], puc[2], puc[3]);
return 0;
}
unsigned char puc[4] 开辟内存大小4个字节,32 个bit,memset(puc, 0, 4)把4个字节内容全设置为零
p = (struct tagPIM*)puc;指向puc开辟内存的首地址
p->a = 2; // 00000010
p->b = 3; //00000011
p->c = 4; // 00000100
p->d = 5; // 00000101
数组中内存由低地址向高地址进行存储。
a不是位段单独占据一个字节,占据前8位,故前8位为00000010
位段b,c,d分别占据1,2,3个bit,所以共用一个字节,占据第二个8位
其中b只有1位空间,只能存储00000011末尾一位1,c只有两位空间,只能存储00000100末尾两位00,d只有3位空间,存储00000101末尾3位101,故第二个8位为00101001。
最后结果由%02x打印,也就是打印两个十六进制位,4个bit位等于1个16进制位则
00000010 ->02
00101001 ->29
00000000 ->00
00000000 ->00
最后输出 02 29 00 00
联合体所在内存的大小
联合的特点
联合的成员是共用同一块内存空间的,这样一个联合变量的大小,至少是最大成员的大小(因为联合至少得有能力保存最大的那个成员)。
联合大小的计算
1.联合的大小至少是最大成员的大小。
2.当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍。
例题1
union A
{
short s[7];
int n;
};
int main()
{
printf("%d",sizeof(union A));
}
short s[7] 所占大小为14个字节 一个short类型大小为2个字节<默认对齐数8 则对齐数为2
int n 占4个字节<默认对齐数8 对齐数为4
得最大对齐数为4
由联合体大小计算可知:最大成员大小14不是最大对齐数4的整数倍,故该联合体大小为16
例题2下面输出结果为?
int main()
{
union
{
short k;
char i[2];
}*s,a;//a为联合体变量
s = &a;
s->i[0] = 0x39;
s->i[1] = 0x38;
printf("%x\n",a.k);
}
枚举
枚举类型定义
{}中的内容是枚举类型的可能取值,也叫枚举常量。这些可能取值都是有值的,默认从0开始,一次递增1,当然在定义的时候也可以赋初值。简单来说如果都没赋初值值从0开始,依次递增1,如果部分赋了初值,后面一位的值为赋了值得那一位的值加1
例题
enum A
{
X1,
Y1,
Z1=255,
A1,
B1,
};
int main()
{
enum A a = Y1;
enum A b = B1;
printf("%d %d\n", a, b);
}
X1没赋初值 默认为0
Y1 X1+1=1
Z1赋了初值255
A1 Z1+1=256
B1 A1+1=257
所以输出:1 257