C语言学习笔记_结构体的内存对齐
C语言学习笔记_结构体的内存对齐
结构体的指针访问和下标访问
结构体可以在定义的同时申明变量:
// 定义的同时申明变量s1;
struct student {
int a;
double b;
char c;
}s1 ;
// 申明s2;
struct student s2;
结构体的下标访问:
s1.a = 1;
s1.b = 2.2;
s1.c = 'x';
printf("s1.a = %d.\n", s1.a);
printf("s1.a = %f.\n", s1.b;
printf("s1.a = %c.\n", s1.c);
结构体的指针访问:
// 第一个变量在开始,所以直接转换结构体的地址为int *;
int *p = (int *)&s1;
*p = 12;
printf("s1.a = %d.\n", s1.a);
// 经过第一个变量到达第二个变量的首地址,第一个变量为int,所以转换为int类型在加4,得到真正的地址;
double *d = (double *)((int)&s1+4);
*d = 3.3;
printf("s1.a = %f.\n", s1.b;
// 和上一个相同,不过double类型的长度为8,所以地址相差4+8=12;
char *c = (char *)((int)&s1+12);
*c = 'y';
printf("s1.a = %c.\n", s1.c);
可以发现结构体的指针访问可以更好的理解结构体的对齐访问;
结构体的对齐访问
结构体中的数据类型长短不一,数据的对齐访问方式牺牲部分内存以提高访问效率,;
在32位系统中采用4字节对齐,有以下特点:
- 结构体本身必须在4字节对齐处;
- 结构体对齐后的大小必须是4的倍数,N字节对齐则为N的整数倍;
- 结构体中每个元素必须对齐存放,每个元素有自身的对齐规则;
- 编译器考虑结构体存放时,以满足以上规则的最少内存消耗策略;
下面是一些对齐访问的案例
struct s1 { // 无对齐 4字节对齐
int a; // 4 4
char b; // 1 2(1+1)
short c; // 2 2
};
sizeof(struct s1); // 7 8
struct s2 { // 无对齐 4字节对齐
char a; // 1 4(1+3)
int b; // 4 4
short c; // 2 4(2+2)
};
sizeof(struct s2); // 7 12
struct s3 { // 无对齐 4字节对齐
int a; // 4 4
struct s1 s; // 7 8
double b; // 8 8
int c; // 4 4
};
sizeof(struct s3); // 23 24
struct student{ // 无对齐 4字节对齐
char sex; // 1 4(1+3)
int length; // 4 4
char name[10]; // 10 12(10+2)
};
sizeof(struct student);// 15 20
gcc的对齐指令
gcc支持但不推荐的对齐指令:
通过pramaga指令和参数k设置一个区间,此区间内为k字节对齐:
// 设置对齐字节为1字节,相当于不对齐
#pragma pack(1)
struct s1 {
int a;
char b;
short c;
};
// 长度为7
printf("sizeof(struct s1) = %d.\n", sizeof(struct s1));
// 结束
#pragma pack
gcc支持且推荐的对齐指令:
在声明结构体的后面加上该指令,只作用于此类型;
// __attribute__((packed))表示取消对齐,只作用于s1;
struct s1 {
int a;
char b;
short c;
} __attribute__((packed));
// __attribute__((aligned(n)))表示结构体设置整体n个字节对齐,这里设置4字节对齐,只作用于s2;
struct s2 {
int a;
char b;
short c;
} __attribute__((aligned(4)));
注意这里的aligned是整体n字节对齐,和前面的对齐以及prama的对齐不一样,前面的对齐是结构体中每个元素按照n字节对齐,而aligned中结构体的元素还是4字节对齐,整体按照n字节对齐;