sizeof函数解析——解决sizeof求结构体大小的问题hyin

C/C++中不同数据类型所占用的内存大小

32位                 64位

char               1                    1

int                  4             大多数4,少数8

short              2                    2

long               4                    8

float               4                    4

double            8                    8

指针                    4                         8

(单位都为字节)

结构体(struct):比较复杂,对齐问题。

联合(union):所有成员中最长的。

枚举(enum):根据数据类型。

sizeof计算单层结构体大小

运算符sizeof可以计算出给定类型的大小,对于32位系统来说,sizeof(char) = 1; sizeof(int) = 4。基本数据类型的大小很好计算,我们来看一下如何计算构造数据类型的大小。

C语言中的构造数据类型有三种:数组、结构体和共用体。

数组是相同类型的元素的集合,只要会计算单个元素的大小,整个数组所占空间等于基础元素大小乘上元素的个数。

结构体中的成员可以是不同的数据类型,成员按照定义时的顺序依次存储在连续的内存空间。和数组不一样的是,结构体的大小不是所有成员大小简单的相加,需要考虑到系统在存储结构体变量时的地址对齐问题。看下面这样的一个结构体:

用sizeof求该结构体的大小,发现值为12。int占4个字节,char占1个字节,结果应该是9个字节才对啊,为什么呢?

先介绍一个相关的概念——偏移量。偏移量指的是结构体变量中成员的地址和结构体变量地址的差。结构体大小等于最后一个成员的偏移量加上最后一个成员的大小。显然,结构体变量中第一个成员的地址就是结构体变量的首地址。因此,第一个成员i的偏移量为0。第二个成员c的偏移量是第一个成员的偏移量加上第一个成员的大小(0+4),其值为4;第三个成员j的偏移量是第二个成员的偏移量加上第二个成员的大小(4+1),其值为5。

然而,在实际中,存储变量时地址要求对齐,编译器在编译程序时会遵循两条原则:

(1)结构体变量中成员的偏移量必须是成员大小的整数倍(0被认为是任何数的整数倍)

(2)结构体大小必须是所有成员大小的整数倍,也即所有成员大小的公倍数。

上面的例子中前两个成员的偏移量都满足要求,但第三个成员的偏移量为5,并不是自身(int)大小的整数倍。编译器在处理时会在第二个成员后面补上3个空字节,使得第三个成员的偏移量变成8。结构体大小等于最后一个成员的偏移量加上其大小,上面的例子中计算出来的大小为12,满足要求。

再来看另外一个例子:

成员k的偏移量为0;成员t的偏移量为4,都不需要调整。但计算出来的大小为6,显然不是成员k大小的整数倍。因此,编译器会在成员t后面补上2个字节,使得结构体的大小变成8从而满足第二个要求。

由此可见,结构体类型需要考虑到字节对齐的情况,不同的顺序会影响结构体的大小。

对比下面两种定义顺序:

虽然结构体stu3和stu4中成员都一样,但sizeof(struct stu3)的值为12而sizeof(struct stu4)的值为8。

sizeof计算嵌套的结构体大小

对于嵌套的结构体,需要将其展开。对结构体求sizeof时,上述两种原则变为:

(1)展开后的结构体的第一个成员的偏移量应当是被展开的结构体中最大的成员的整数倍。

(2)结构体大小必须是所有成员大小的整数倍,这里所有成员计算的是展开后的成员,而不是将嵌套的结构体当做一个整体。

看下面的例子:

结构体stu5的成员ss.c的偏移量应该是4,而不是2。整个结构体大小应该是16。

下述代码测试原则2:

结构体ss单独计算占用空间为8,而stu5的sizeof则是20,不是8的整数倍,这说明在计算sizeof(stu5)时,将嵌套的结构体ss展开了,这样stu5中最大的成员为ss.j,占用4个字节,20为4的整数倍。如果将ss当做一个整体,结果应该是24了。

另一个特殊的例子是结构体中包含数组,其sizeof应当和处理嵌套结构体一样,将其展开,如下例子:

其值为20。float占4个字节,到char p时偏移量为4,p占一个字节,到int adf[3]时偏移量为5,扩展为int的整数倍,而非int adf[3]的整数倍,这样偏移量变为8,而不是12。结果是8+12=20,是最大成员float或int的大小的整数倍。

如何给结构体变量分配空间由编译器决定,以上情况针对的是Linux下的GCC。在Windows下的VC平台也是这样,至于其他平台,可能会有不同的处理。

THE END
0.C++构造函数详解:基础分类与实践应用1、构造函数名称和类的名称一致(包括大小写); 2、构造函数没有返回类型,也没有void类型返回;(同时,析构函数也一样) 3、构造函数可以对成员变量进行初始化 构造函数的分类 构造函数根据入参类型的不同,有不同的分类。这种特性,是基于函数重载的使用。 jvzquC41dnuh0lxfp0tfv8|gkzooa=8:269828ftvkimg8igvcomu8645;834<=
1.类型大小与构造函数int类型,构造函数 不同平台下int类型、指针类型的数据大小 对于int类型数据和指针类型数据的大小,是非常基础的问题。 在一个具体的平台上,确定他们最好的办法就是使用sizeof(type)对其进行判断,返回当前数据类型的大小。 在不同的平台下,int类型和指针类型的数据类型大小时怎样的呢?如果要给出一个统一的答案,自然jvzquC41dnuh0lxfp0tfv8|gkzooa=8;778:58ftvkimg8igvcomu86446997B;
2.冲压模具依构造可分为单工程模复合模连续模三大类本文详细介绍了冲压模具的构造分类,包括单工程模、复合模和连续模,强调了连续模在效率上的优势。文章还探讨了模具设计的单元化概念,模板的构成和规格,以及各种模板设计的细节,如整块式、轭式和镶入式。此外,文中还涵盖了模具对准单元、导引系统、冲头与母模单元等关键设计要素,为五金冲压模具设计提供了深入的理解。 jvzquC41dnuh0lxfp0tfv8Ytqw|bkuqgac5bt}neng5eg}fknu523@=279:4
3.堆数据结构详解:性质分类排序及构造算法如何构造一个大堆 堆的性质 堆中某个节点的值总是不大于或者不小于其父节点的值 堆是一颗完全二叉树 堆的分类 小堆每个节点的值都小于或者等于它的左右孩子节点的值。 大堆每个节点的值都大于或者等于它的左右孩子节点的值。 两种结构在数组中是如何储存的如下图: jvzquC41dnuh0lxfp0tfv87523e92=;:3380c{ykenk0fnyckny03<;;37949
4.气瓶基础知识一、按构造分类(共分为五类) 1、无缝气瓶 1)通用的无缝气瓶的形式为:筒体呈圆柱形,一端为凸形、凹形或H形的瓶底,而另一端为带颈的球形瓶肩。在瓶颈上面有一个带锥形螺纹的瓶口,用来装配瓶阀。 常用的瓶体形式有:1)凹形底;2)凸形带底座;3)凸形底;4)H形底;5)无底双口形。 jvzquC41yy}30|hwv0kew7hp1nynr8723;524:61e4:47
5.一文了解|「以假乱真」的人工血管(二)按照构造分类 管状人工血管:具有圆柱形状,类似自然血管的外观和结构。 束状人工血管:由一束纤维束或纤维编织而成,可以提供多个通道,用于支持血液流动。 薄膜人工血管:由薄膜形态的材料制成,常用于修复小血管或用作血管修复的辅助材料。 (三)按照应用领域分类 jvzquC41yy}/kwsqof4ptp4ctvodnn486f;g9?j:hg:1f9523f?e;o6
6.C++构造与析构详解构造函数和析构函数 本文详细介绍了C++中的构造函数和析构函数的概念、分类及其调用规则。包括构造函数的不同形式(无参数、有参数、拷贝构造)、初始化列表的使用,以及对象动态管理和深拷贝浅拷贝的区别。 对象的构造和析构 1.基本概念   创建一个对象时,常常需要作某些初始化的工作,例如对数据成员赋初值。jvzquC41dnuh0lxfp0tfv8hqqn}sk}jt1cxuklqg1fkucrqu19>73?782