C语言结构体对齐
1.结构体变量中的元素如何访问?
(1)数组中元素的访问方式:表面上有2种方式(数组下标方式和指针方式);实质上都是指针方式访问。
(2)结构体变量中的元素访问方式:只有一种,用.或者->的方式来访问。(.和->访问结构体元素其实质是一样的,只是C语言规定用结构体变量来访问元素用. 用结构体变量的指针来访问元素用->。实际上在高级语言中已经不区分了,都用.)
(3)结构体的访问方式有点类似于数组下标的方式
2.利用指针访问结构体元素
结构体元素地址=结构体首地址+元素偏移量
struct mystruct { int a; int b; } s1; //s1.b的地址 int* p=(int*)((int)&s1+4); *p=5;// s1.b=5;
3.结构体对齐
一般情况下,为了配合硬件,如果对齐排布和访问会提高效率,否则会大大降低效率。
(0)、一般编辑器默认4字节对齐。
(1)、结构体对齐要考虑:结构体整体本身必须安置在4字节对齐处,结构体对齐后的大小必须4的倍数(编译器设置为4字节对齐时,如果编译器设置为8字节对齐,则这里的4是8)
(2)、结构体中每个元素本身都必须对其存放,而每个元素本身都有自己的对齐规则。
(3)、编译器考虑结构体存放时,以满足以上2点要求的最少内存需要的排布来算。
struct mystruct1 { // 1字节对齐 4字节对齐 int a; // 4 4 char b; // 1 2(1+1) short c; // 2 2 }; struct mystruct11 { // 1字节对齐 4字节对齐 int a; // 4 4 char b; // 1 2(1+1) short c; // 2 2 }; typedef struct mystruct111 { // 1字节对齐 4字节对齐 2字节对齐 int a; // 4 4 4 char b; // 1 2(1+1) 2 short c; // 2 2 2 short d; // 2 4(2+2) 2 } My111; typedef struct mystruct2 { // 1字节对齐 4字节对齐 char a; // 1 4(1+3) int b; // 4 4 short c; // 2 4(2+2) }MyS2; struct mystruct21 { // 1字节对齐 4字节对齐 char a; // 1 4(1+3) int b; // 4 4 short c; // 2 4(2+2) } ; typedef struct myStruct5 { // 1字节对齐 4字节对齐 int a; // 4 4 struct mystruct1 s1; // 7 8 double b; // 8 8 int c; // 4 4 }MyS5; struct stu { // 1字节对齐 4字节对齐 char sex; // 1 4(1+3) int length; // 4 4 char name[10]; // 10 12(10+2) };
4.结构体对齐指令
以#prgama pack(n)开头,以#pragma pack()结尾,定义一个区间,这个区间内的对齐参数就是n。
#pragma pack(2) struct s { char c; //2 (1+1) int b; //4 (4) } ; #pragma pack()
取消对齐访问
__attribute__((packed));
设置结构体整体对齐访问(不包含元素)
__attribute__((aligned(n)));
struct mystruct11 { // 1字节对齐 4字节对齐 int a; // 4 4 char b; // 1 2(1+1) short c; // 2 2 }__attribute__((packed)); typedef struct mystruct111 { // 1字节对齐 4字节对齐 2字节对齐 int a; // 4 4 4 char b; // 1 2(1+1) 2 short c; // 2 2 2 short d; // 2 4(2+2) 2 }__attribute__((aligned(1024))) My111;
5.offsetof宏与container_of宏
offsetof 在0地址处虚拟出一个结构体,通过元素的地址则为偏移量
container_of 通过元素地址减去偏移量得到结构体地址
typeof()传入变量,返回相应的数据类型。
#include<stdio.h> struct mystruct { char a; int b; short c; }; // TYPE是结构体类型,MEMBER是结构体中一个元素的元素名 // 这个宏返回的是member元素相对于整个结构体变量的首地址的偏移量,类型是int #define offsetof(TYPE, MEMBER) ((int) &(((TYPE *)0)->MEMBER))//TYPE *为传入值 // ptr是指向结构体元素member的指针,type是结构体类型,member是结构体中一个元素的元素名 // 这个宏返回的就是指向整个结构体变量的指针,类型是(type *) //typeof() 是通过变量名 返回数据类型的 #define container_of(ptr, type, member) ({ \ const typeof(((type *)0)->member) * __mptr = (ptr); \ //定义一个typeof(((type *)0)->member)类型的指针指向 结构体元素地址 (type *)((char *)__mptr - offsetof(type, member)); }) //元素地址-偏移量=结构体首地址 //通过元素地址减去偏移量得到结构体地址 int main() { struct mystruct s1; struct mystruct *Ps; s1.b=12; int * pb=&s1.b; Ps=container_of(pb,struct mystruct,b); printf("container_of宏实例\n"); printf("&s1=%p\n",&s1); printf("Ps=%p\n",Ps); //int *p=(int *)((char*)&s1+4); int *p=(int *)((int)&s1+4); printf("*p=%d.\n",*p); printf("offsetof宏实例\n"); int offsetof_a=offsetof(struct mystruct,a); int offsetof_b=offsetof(struct mystruct,b); int offsetof_c=offsetof(struct mystruct,c); printf("offsetof_a=%d\n",offsetof_a); printf("offsetof_b=%d\n",offsetof_b); printf("offsetof_c=%d\n",offsetof_c); return 0; }