c/c++ 字节对齐
程序员文章站
2022-04-01 19:41:49
c 字节对齐 概念: 结构体里会包括各种类型的成员,比如int char long等等,它们要占用的空间不同,系统为一个结构体开辟内存空间时,会有2种选择。 第一种:节省空间的方案,以上面的列子来说的话,就是4(int) + 1(char) + 8(long) =13个字节; 第二种:浪费空间的方案 ......
c 字节对齐
概念:
结构体里会包括各种类型的成员,比如int char long等等,它们要占用的空间不同,系统为一个结构体开辟内存空间时,会有2种选择。
- 第一种:节省空间的方案,以上面的列子来说的话,就是4(int) + 1(char) + 8(long) =13个字节;
- 第二种:浪费空间的方案,以上面的列子来说的话,就是4(int) + 4(char) + 8(long) =16个字节;
其实,系统是用的第二种方案。
字节对齐的目的:
为了CPU只寻找地址一次,就能够把目标内存中的数据取出来。
现代计算机中内存空间都是按照byte划分的 ,如果是用第一种节省空间的方案,为了要取一个int或者long的成员的值,CPU寻址一次,可能只取出来一部分,所以需要再次寻址,这样就导致CPU的效率降低。为了提高CPU的效率,所以选择牺牲空间,但是节省了时间。
经验总结
声明结构体成员的时候,一定把占用空间最小的类型放在最前面,占用空间最大的放在最后面,这样就会节省内存空间。
程序验证
#include <stdio.h> typedef struct A{ char a; char b; char c; }A; //#pragma pack(4) //如果把注释打开就是强制按4字节对齐,所以就是16 typedef struct B{//24 int a;//占用4字节 + 4 //往下看,下面的8字节,所以补4个字节 long b;//占用8字节 char c;//占用1字节+ 7 // 4+4+8+1=9,但9不是8的倍数,所以在最后的char c处加7个字节 }B; typedef struct C{//16 char a;//1 + 3 int b;//4 long c;//8 }C; struct D{//32 int a;//4 + 4 //往下看,下面的8字节,所以补4个字节 struct S1{//16 long b;//8 char c;//1 + 1 //往下看,下面的2字节,所以补1个字节 short d;//2 + 4 //8+2+2=12但不是8的倍数,所以在最后的short d处加4个字节 } aa; int e;//4 + 4 //8+16+4=28但不是8的倍数,所以在最后的int e处加4个字节 }D; struct E{//40 int a;//4 + 4 struct S2{//24 short d;//2 + 6 //往下看,下面的8字节,所以补6个字节 long b;//8 char c;//1 + 7 //8+8+1=17但不是8的倍数,所以在最后的char c处加7个字节 } aa; int e;//4 + 4 ////8+24+4=36但不是8的倍数,所以在最后的int e处加4个字节 }E; struct F{//72 int a;//4 + 4 struct {//56 short d[20];//40 //不管数组里多少个元素,只看数组的类型 long b;//8 char c;//1 + 7 } ; int e;//4 + 4 }F; int main(){ printf("A size = %ld\n",sizeof(A)); printf("B size = %ld\n",sizeof(B)); printf("C size = %ld\n",sizeof(C)); printf("D size = %ld\n",sizeof(D)); printf("E size = %ld\n",sizeof(E)); printf("F size = %ld\n",sizeof(F)); B B1; B1.a = 10; B1.b = 12; B1.c = 'A'; }
运行结果:
A size = 3 B size = 24 C size = 16 D size = 32 E size = 40 F size = 72
位域
变量可以按比特位进行定义,比如定义只占用1个比特位的char c:1;
只占用10个比特位的int i:10;
定义的比特位不可以超过类型自身所占用的字节数
int i:33;//编译不过,因为int占用4个字节,32个比特位,所以最多定义到int i:32;
#include <stdio.h> struct A{//char占用1个字节8个比特位,a b c各占用1个比特位,总共3个比特位,小于8个比特位,所以这个结构体占用1个字节 char a:1; char b:1; char c:1; }A; struct B{//char占用1个字节8个比特位,a b c总共占用8个比特位,正好是1个字节,所以这个结构体占用1个字节 char a:2; char b:2; char c:4; }B; struct B1{//char占用1个字节8个比特位,a b c总共占用9个比特位,超过1个字节,所以这个结构体占用2个字节 char a:2; char b:2; char c:5; }B1; struct C{//int占用4个字节32个比特位,a b总共占用32个比特位,正好4个字节,所以这个结构体占用4个字节 int a:31; char b:1; }C; struct C1{//int占用4个字节32个比特位,a b总共占用33个比特位,超过4个字节,所以这个结构体占用8个字节,因为在char b处需要补3个字节 int a:31; char b:2; }C1; struct C2{//即使结构体C2看起来只占用3个比特位,但是成员a是int类型,所以这个结构体占用4个字节 int a:1; char b:2; }C2; int main(){ printf("A:%ld\n", sizeof(A)); printf("B:%ld\n", sizeof(B)); printf("B1:%ld\n", sizeof(B1)); printf("C:%ld\n", sizeof(C)); //结构体C里的成员a占用32个比特位的后31个比特位;成员b占用第1个比特位 struct C sc; sc.a =10; sc.b = 1; } GDB的调试结果: 40 sc.a =10; (gdb) p &sc//打印出sc.a的地址 $1 = (struct C *) 0x7fffffffdecc (gdb) x/tw 0x7fffffffdecc//用二进制方式,产看内存地址里的值 0x7fffffffdecc: 00000000000000000000000000000000 (gdb) n 41 sc.b = 1; (gdb) x/tw 0x7fffffffdecc//发现10被放到内存中了,1010就是十进制的10 0x7fffffffdecc: 00000000000000000000000000001010 (gdb) n 42 } (gdb) x/tw 0x7fffffffdecc//发现1被放到内存的第一个比特位上了 0x7fffffffdecc: 10000000000000000000000000001010
运行结果:
A:1 B:1 B1:2 C:4 C1:8 C2:4
下一篇: 路由器也需要重启?你不知道的联网妙招