热门标签 | HotTags
当前位置:  开发笔记 > IOS > 正文

详解C++sizeof(下)

这篇文章主要介绍了C++sizeof的相关资料,帮助大家更好的理解和学习c++,感兴趣的朋友可以了解下

sizeof作用于基本数据类型,在特定的平台和特定的编译器中,结果是确定的,如果使用sizeof计算构造类型:结构体、联合体和类的大小时,情况稍微复杂一些。

1.sizeof计算结构体

考察如下代码:

struct S1
{
char c;
int i;
};
cout<<”sizeof(S1)=”<

sizeof(S1)结果是8,并不是想象中的sizeof(char)+sizeof(int)=5。这是因为结构体或类成员变量具有不同类型时,需进行成员变量的对齐。《计算机组成原理》一书中说明,对齐的目的是减少访存指令周期,提高CPU存储速度。

1.1内存对齐原则

(1)结构体变量的首地址能够被其最宽基本成员类型大小所整除;

(2)结构体每个成员相对于结构体首地址的偏移量都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节;

(3)结构体的总大小为结构体最宽基本成员类型大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节。

有了以上三个内存对齐的原则,就可以轻松应对嵌套结构体类型的内存对齐。如下:

struct S2
{
char c1;
S1 s;
char c2;
};

在寻找S2的最宽基本数据类型时,包括其嵌套的结构体中的成员,从S1中寻找出最宽结构体数据类型是int,因此S2的最宽数据类型是int。S1 s在结构体S2中的对齐也遵守前三个准则,因此sizeof(S2)=sizeof(char)+pad(3)+sizeof(S1)+1+pad(3)=1+3+8+1+3=16字节,其中pad(3)表示填充3个字节。

结构体某个成员相对于结构体首地址的偏移量可以通过宏offsetof()来获得,这个宏也在stddef.h中定义,如下:

#define offsetof(s,m) (size_t)&(((s *)0)->m)

例如获得S1中的偏移量,方法为

size_t pos = offsetof(S1, i); //pos等于4

1.2修改对齐方式

1.2.1#pragma pack

#pragma pack(n)中n为字节对齐数,其取值为1、2、4、8、16,默认是8。结构体对齐时,

(1)成员的偏移量为成员本身大小和n二者最小值的整数倍;
(2)结构体最终大小是结构体中最宽基本类型成员大小和n二者中的最小值的整数倍。

考察如下代码:

#pragma pack(push) //将当前pack设置压栈保存
#pragma pack(2) //必须在结构体定义之前使用
struct S1
{
char c;
int i;
};
struct S2
{
char c1;
S1 s;
char c2
};
#pragma pack(pop) // 恢复先前的pack设置

//或者
#pragma pack(2)
...
#pragma pack()

因此,sizeof(S2)=sizeof(char)+pad(1)+sizeof(S1)+1+pad(1)=1+1+6+1=10字节。

注意,#pragma pack不能指定变量的存储地址,变量的首地址默认为最大基本成员类型大小的整数倍。

1.2.2__declspec(align(#))

VC++支持__declspec(align(#)),在GNU C++并不支持。#的取值为1~8192,为2的幂。使用示例如下:

__declspec(align(256)) struct TestSize
{
char a;
int i;
};
cout<

__declspec(align(#))要求#为2的整数次幂,作用主要有两个方面:
(1)使结构体或类成员按#pragma pack确定内存布局之后,在末尾填充内存使得整个对象的大小至少是#的整数倍。
(2)作用于变量时,强制要求编译器将变量放置在地址是#整数倍的内存位置上。这点在调用原生API等要求严格对齐的方法时十分重要。

1.3空结构体

C/C++中不允许长度为0的数据类型存在。对于“空结构体”(不含数据成员)的大小不为0,而是1。“空结构体”变量也得被存储,这样编译器也就只能为其分配一个字节的空间用于占位了。如下:

struct S3 { };
sizeof(S3); // 结果为1

1.4位域结构体

有些信息在存储时,并不需要占用一个完整的字节, 而只需占一个或多个二进制位。例如在存放一个开关量时,只有0和1 两种状态, 用一位即可表示。为了节省存储空间,并使处理简便,C语言又提供了一种数据结构,称为”位域”或”位段”。包含位域变量的结构体叫作位域结构体。位域结构体的定义形式:

struct 位域结构体名
{ 
类型说明符 位域名:位域长度;
...
};

注意,位域长度不应该大于该类型说明符对应的数据类型的位长度。
使用位域的主要目的是压缩存储,其大致规则为:

(1)如果相邻位域字段的类型相同,且其位宽之和小于类型的sizeof大小,则后面的字段将紧邻前一个字段存储,直到不能容纳为止;
(2)如果相邻位域字段的类型相同,但其位宽之和大于类型的sizeof大小,则后面的字段将从新的存储单元开始,其偏移量为其类型大小的整数倍;
(3)如果相邻位域字段的类型不同,则各编译器的具体实现有差异,VC++采取不压缩方式,GNU C++采取压缩方式;
(4)如果位域字段之间穿插着非位域字段,则不进行压缩;
(5)整个结构体的总大小为最宽基本类型成员大小的整数倍;
(6)位域可以无位域名,这时它只用作填充或调整位置,不能使用。例如:

struct BitFiledStruct
{ 
int a:1;
int :2; //该2位不能使用
int b:3;
int c:2;
};

关于位域结构体的sizeof大小,考察如下代码:

#include 
using namespace std;

struct BFS1
{
char f1 : 3;
char f2 : 4;
char f3 : 5;
};
struct BFS2
{
char f1 : 3;
int i : 4;
char f2 : 5;
};
struct BFS3
{
char f1 : 3;
char f2;
char f3 : 5;
};

int main()
{
cout<

运行上面的程序,VC++和GNU C++输出结果如下:

//VC++输出结果
2
12
3

//GNU C++输出结果
2
4
3

考察以上代码,得出:

(1)sizeof(BFS1)==2。当相邻位域类型不同,在VC++中sizeof(BFS2)=1+pad(3)+4+1+pad(3)=12,采用不压缩方式,位域变量i的偏移量需要是4的倍数,并且位域结构体BFS2的总大小必须是sizeof(int)的整数倍。在GNU C++中为sizeof(BFS2)=4,相邻的位域字段的类型不同时,采取了压缩存储,位域变量i紧随位域变量f1的剩余位进行存储,位域变量f2同样是紧随位域变量i的剩余位进行存储,并且位域结构体BFS2的总大小必须是sizeof(int)的整数倍,所以最终结果sizeof(BFS2)=1+pad(3)=4。

(2)sizeof(BFS3)==3,当非位域字段穿插在其中,不会产生压缩,在VC++和GNU C++中得到的大小均为3,如果压缩存储,则sizeof(BFS3)==2。

2.sizeof计算共用体

结构体在内存组织上是顺序式的,共用体则是重叠式,各成员共享一段内存,所以整个共用体的sizeof也就是每个成员sizeof的最大值。结构体的成员也可以是构造类型,这里,构造类型成员是被作为整体考虑的。所以,下面例子中,假设sizeof(s)的值大于sizeof(i)和sizeof(c),那么sizeof(U)等于sizeof(s)。

union U
{
int i;
char c;
S1 s;
};

3.sizeof计算类

类是C++中常用的自定义构造类型,有数据成员和成员函数组成,进行sizeof计算时,和结构体并没有太大的区别。考察如下代码:

#include 
using namespace std;

class Small{};

class LessFunc
{
int num;
void func1(){};
};

class MoreFunc
{
int num;
void func1(){};
int func2(){return 1;};
};

class NeedAlign
{
char c;
double d;
int i;
};

class Virtual
{
int num;
virtual void func(){};
};

int main(int argc,char* argv[])
{
cout<

注意一点,C++中类同结构体没有本质的区别,结构体同样可以包含成员函数,构造函数,析构函数,虚函数和继承,但一般不这么使用,沿用了C的结构体使用习惯。类与结构体唯一的区别就是结构体的成员的默认权限是public,而类是private。

基于以上这点,再考察从程序的输出结果,得出如下结论:

(1)类同结构体一样,C++中不允许长度为0的数据类型存在,虽然类无任何成员,但该类的对象仍然占用1个字节。
(2)类的成员函数并不影响类对象占用的空间,类对象的大小是由它数据成员决定的。
(3)类和结构体一样,同样需要对齐,具体对齐的规则见上文结构体的内存对齐。
(4)类如果包含虚函数,编译器会在类对象中插入一个指向虚函数表的指针,以帮助实现虚函数的动态调用。

所以,该类的对象的大小至少比不包含虚函数时多4个字节。如果考虑内存对齐,可能还要多些。如果使用数据成员之间的对齐,当类对象至少包含一个数据成员,且拥有虚函数,那么该对象的大小至少是8B,读者可自行推导。

以上就是详解C++ sizeof(下)的详细内容,更多关于C++ sizeof的资料请关注其它相关文章!


推荐阅读
  • 本文介绍了如何使用jQuery根据元素的类型(如复选框)和标签名(如段落)来获取DOM对象。这有助于更高效地操作网页中的特定元素。 ... [详细]
  • C++实现经典排序算法
    本文详细介绍了七种经典的排序算法及其性能分析。每种算法的平均、最坏和最好情况的时间复杂度、辅助空间需求以及稳定性都被列出,帮助读者全面了解这些排序方法的特点。 ... [详细]
  • C++: 实现基于类的四面体体积计算
    本文介绍如何使用C++编程语言,通过定义类和方法来计算由四个三维坐标点构成的四面体体积。文中详细解释了四面体体积的数学公式,并提供了两种不同的实现方式。 ... [详细]
  • 本文介绍如何通过Windows批处理脚本定期检查并重启Java应用程序,确保其持续稳定运行。脚本每30分钟检查一次,并在需要时重启Java程序。同时,它会将任务结果发送到Redis。 ... [详细]
  • 如何优化2060显卡设置以提升《Apex英雄》游戏体验
    《Apex英雄》作为一款热门的战术竞技游戏,吸引了大量玩家。本文将探讨如何通过优化GeForce RTX 2060显卡设置,确保在《Apex英雄》中获得最佳性能和流畅的游戏体验。 ... [详细]
  • 本章将深入探讨移动 UI 设计的核心原则,帮助开发者构建简洁、高效且用户友好的界面。通过学习设计规则和用户体验优化技巧,您将能够创建出既美观又实用的移动应用。 ... [详细]
  • 本文介绍如何通过SQL查询从JDE(JD Edwards)系统中提取所有字典数据,涵盖关键表的关联和字段选择。具体包括F0004和F0005系列表的数据提取方法。 ... [详细]
  • 如何高效创建和使用字体图标
    在Web和移动开发中,为什么选择字体图标?主要原因是其卓越的性能,可以显著减少HTTP请求并优化页面加载速度。本文详细介绍了从设计到应用的字体图标制作流程,并提供了专业建议。 ... [详细]
  • 本文详细介绍了如何通过命令行启动MySQL服务,包括打开命令提示符窗口、进入MySQL的bin目录、输入正确的连接命令以及注意事项。文中还提供了更多相关命令的资源链接。 ... [详细]
  • 扫描线三巨头 hdu1928hdu 1255  hdu 1542 [POJ 1151]
    学习链接:http:blog.csdn.netlwt36articledetails48908031学习扫描线主要学习的是一种扫描的思想,后期可以求解很 ... [详细]
  • 本文将详细介绍在Windows 7环境下,检查U盘启动盘是否制作成功的多种方法,包括通过BIOS设置和使用模拟启动工具。 ... [详细]
  • 本文介绍如何使用 NSTimer 实现倒计时功能,详细讲解了初始化方法、参数配置以及具体实现步骤。通过示例代码展示如何创建和管理定时器,确保在指定时间间隔内执行特定任务。 ... [详细]
  • 本文介绍了在Windows环境下使用pydoc工具的方法,并详细解释了如何通过命令行和浏览器查看Python内置函数的文档。此外,还提供了关于raw_input和open函数的具体用法和功能说明。 ... [详细]
  • 题目Link题目学习link1题目学习link2题目学习link3%%%受益匪浅!-----&# ... [详细]
  • 本文探讨了 C++ 中普通数组和标准库类型 vector 的初始化方法。普通数组具有固定长度,而 vector 是一种可扩展的容器,允许动态调整大小。文章详细介绍了不同初始化方式及其应用场景,并提供了代码示例以加深理解。 ... [详细]
author-avatar
杨支榕_293
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有