
摘要:
经常被计算结构体的sizeof给搞晕,于是找了个时间,静下心来,搞定它。
一、为什么结构体计算这样乱?
答案是字符对齐,计算机内存系统中以Byte为单位存储数据,不同数据种类所占的空间不同,如:整型(int)数据占4个字节,字符型(char)数据占一个字 节,

短整型(short)数据占两个字节,等等。计算机为了迅速的读写数据,默认状况下将数据保存在某个地址的起始位置,如:整型数据(int)默认内存 在地址能被
4整除的起始位置,字符型数据(char)可以存放在任何地址位置(被1整除),短整型(short)数据存储在地址能被2整除的起始位置。这样字节对齐有助于加快
计算机的取数速度,否则就得多花指令周期了。
二、字节对齐的细节和准确编译器实现相关,但一般而言,满足三个准则:

1. 结构体变量的首地址无法被其最宽基本种类成员的大小所整除;
2. 结构体每个成员相对于结构体首地址的偏移量都是当前成员大小的整数倍,如有需要编译器会在成员之间加上填充字节;
3. 结构体的总大小为结构体最宽基本种类成员大小的整数倍,如有需要编译器会在最末一个成员后来加上填充字节。
说明:1、基本类别是指上面看到的像char、short、int、float、double这样的内置数据类型;

2、对于复合数据种类,如结构体嵌套结构体,那么基本类型是指上面看到的像char、short、int、float、double这样的内置数据类型;
3、我觉得计算结构体大小的之后,主要用到准则2和准则3计算结构体大小,对于准则1是编译器自动完成的,不需要过多理会。
4、C++中类的可以看做是特殊的结构体,所以类的sizeof的推导和结构体是一样的。
三、下面拿具体的程序来详细表明:

1 // sizeof(结构体).cpp : 定义控制台应用程序的入口点。 2 // 3 4 #include "stdafx.h" 5 #include<iostream> 6 using namespace std; 7 8 struct A 9 { 10 char a; 11 int b; 12 }; 13 struct B 14 { 15 char a; 16 A a1; 17 char b; 18 }; 19 int _tmain(int argc, _TCHAR* argv[]) 20 { 21 cout<<sizeof(A)<<endl<<sizeof(B); 22 return 0; 23 }
上述程序的输出结果是8和16.
下面结合上面给出的准则具体剖析一下。
对于结构体A其基本成员种类有char和int两种,最宽的是int占用4个字节,那么按照准则1,编译器会手动为结果体A分配一个能被4整除的首地址,A的第一个成员char的首地址就是结构体A的首地址,即偏移量为0,接下来,下一个成员变量int首地址,如果不做处理的话,应该是相对于A的偏移量是1,这就不满足准则2了,所以编译器开始在char的前面填充3个字节,使得int相对于A的偏移量是4,来满足准则2,然后结构体的总大小就是1(char)+3(填充)+4(int)=8,同时满足了准则3,不用再填充了计算结构体大小,所以sizeof(A)=8。
对于结构体B,其中包含了一个复合类型,查看基本类型的之后,要将其中的结构体A拆分成char和int两种类别来看,所以结构体B中的基本数据类别是char,char,int,char,最宽的数据类别是int,编译器会手动为B分配一个能被4整除的首地址,B的第一个成员char的首地址就是结构体B的首地址,即偏移量为0,接下来,下一个成员变量A的首地址,如果不做处理的话,应该是相对于B的偏移量是1,这就不满足准则2了,所以编译器开始在char的前面填充3个字节,使得成员A相对于所在结构体B的地址偏移量是4,来满足准则2,这时加上B的长度8,B中最后一个成员char相对于B来说地址偏移量是8,能满足准则2,所以不需要在A的后边填充字节,这时B的总长度是1(char)+3(填充)+8(A)+1(char)=13,不满足准则3,所以还必须在最后一个char后面再添加3个字节,最后得到B的总大小是1(char)+3(填充)+8(A)+1(char)+3(填充)=16,所以sizeof(B)=16。
本文来自电脑杂谈,转载请注明本文网址:
https://www.pc-fly.com/a/jisuanjixue/article-125629-1.html
一样的价格
当年electricshock的精灵们回到曾经的森林去寻找回忆
撞也不好撞