结构体字节对齐方式
内存对齐规则
- 变量(结构体变量)的起始地址能够被其对齐值整除,结构体变量的对齐值为最宽的成员大小
- 结构体每个成员相对于起始地址的偏移能够被其自身对齐值整除,如果不能则在前一个成员后面补充字节
- 结构体总体大小能够被最宽的成员的大小整除,如不能则在后面补充字节
此外还有编译器的默认对齐值,一般默认对齐值为4(结构体的实际对齐值会取结构体对齐值和编译器默认对齐值中较小的那一个)。
内存对齐好的处
- 为了减少使用的内存
- 为了提升数据读取的效率
#include <iostream> using namespace std; struct A { char c; short a; int i; }; int main() { A c; cout<<alignof(c.a)<<endl;//获取字节对齐方式 cout<<sizeof(A)<<endl; return 0; } //8
#include <iostream> using namespace std; struct A { char c; int i; short a; }; int main() { A c; cout<<alignof(c.a)<<endl;//获取字节对齐方式 cout<<sizeof(A)<<endl; return 0; } //12
double类型的数组(double类型为8字节对齐), 其在内存中所处的位置如下
数组的首地址为2,根据原则1数组未对齐。若CPU每次从内存中为8字节整数倍的地址开始读入8字节的数据,则每次从未对齐的数组中读取一个成员都要进行两次读取操作,而从对齐的数组中读取则只需要一次读取操作,数组对齐时读取效率有较大提升
使用位域的主要目的是压缩存储,其大致规则为:
- 如果相邻位域字段的类型相同,且其位宽之和小于类型的sizeof大小,则后面的字段将紧邻前一个字段存储,直到不能容纳为止;
- 如果相邻位域字段的类型相同,但其位宽之和大于类型的sizeof大小,则后面的字段将从新的存储单元开始,其偏移量为其类型大小的整数倍;
- 如果相邻的位域字段的类型不同,则各编译器的具体实现有差异,VC6采取不压缩方式,Dev-C++采取压缩方式;
- 如果位域字段之间穿插着非位域字段,则不进行压缩;
- 整个结构体的总大小为最宽基本类型成员大小的整数倍。
typedef struct AA{ unsigned char b1:5; unsigned char b2:5; unsigned char b3:5; unsigned char b4:5; unsigned char b5:5; }AA;/*sizeof(AA) = 5*/ typedef struct BB { unsigned int b1:5; unsigned int b2:5; unsigned int b3:5; unsigned int b4:5; unsigned int b5:5; }BB;/*sizeof(BB) = 4*/ typedef struct CC { int b1:1; int :2;//无影响 int b3:3; int b4:2; int b5:3; short b6:4; int b7:1; }CC; /*sizeof(CC) = 12*/
大端:高字节在起始地址(也就是低地址)
小端:低字节在起始地址(也就是低地址)
#include <stdio.h> #include <stdlib.h> using namespace std; int main() { union Test { int num; char str[4]; }T; T.num = 0x12345678; printf("%0x %0x %0x %0x\n", T.str[0], T.str[1], T.str[2], T.str[3]); printf("%p\n",&T.num); printf("%p\n",&T.str[0]); printf("%p\n",&T.str[1]); printf("%p\n",&T.str[2]); printf("%p\n",&T.str[3]); return 0; }