当前位置: 首页 > news >正文

C/C++ 详谈结构体大小计算(内存对齐)

目录

 1. 默认的对齐规则:

几个例子与结果:

2. 修改默认对齐数:

例子: 

3. C++继承场景下的类的大小的计算:

1. 包含虚函数的类

2. 包含成员函数的类

4. 扩展:

定义一个计算成员变量在类中偏移量的宏

做法:

疑问:


 1. 默认的对齐规则:

1. 结构体的第⼀个成员对⻬到和结构体变量起始位置偏移量为0的地址处 其他成员变量要对⻬到某个数字(对⻬数)的整数倍的地址处。

2. 对⻬数 = 编译器默认的⼀个对⻬数 与 该成员变量⼤⼩的较⼩值

        - Visual Studio 中默认的值为 8

        - Linux中 gcc 没有默认对⻬数,对⻬数就是成员⾃⾝的⼤⼩

3. 结构体总⼤⼩为最⼤对⻬数(结构体中每个成员变量都有⼀个对⻬数,所有对⻬数中最⼤的)的 整数倍。

4. 如果嵌套了结构体的情况,嵌套的结构体成员对⻬到⾃⼰的成员中最⼤对⻬数的整数倍处,结构 体的整体⼤⼩就是所有最⼤对⻬数(含嵌套结构体中成员的对⻬数)的整数倍。

几个例子与结果:

struct ss1
{char _c;int _i;int _i2;
};
struct ss2
{int _i;char _c;
};
struct ss3
{int _i;char _c;double _d;
};
struct ss4
{int _i;char _c;struct ss3 s;double _d;
};

2. 修改默认对齐数:

#pragma pack(1) // 设置默认对齐数为1#pragma pack()  // 取消前面设置的默认对齐数

例子: 

#pragma pack(1)
struct test
{int _i;char _c;
};
#pragma pack()
struct ss1
{char _c;int _i;int _i2;
};
int main()
{printf("%d\n", sizeof(struct test));printf("%d\n", sizeof(struct ss1));return 0;
}

在C++中,如果一个结构体(类)中,没有任何成员变量只有成员函数,他的大小是1,因为总要标识这个对象在哪~

3. C++继承场景下的类的大小的计算:

1. 包含虚函数的类

最后说一下在继承的场景下类的大小,我们直接用结果来解释原理~

struct base
{int _i;char _c;virtual void testFunc(){ }
};
struct drive : public base
{double _d;void testFunc(){}
};
struct base1
{int _i;char _c;void testFunc(){ }
};
struct drive1 : public base1
{double _d;
};

这里为什么base的大小是12呢?

4 + 1 + 填充3 + vptr(32位下4字节/64位下8字节)

因为类中有一个虚函数,编译器会为对象隐式添加一个vptr虚函数指针,指向这个对象的虚函数表。

2. 包含成员函数的类

struct test
{int _i;char _c;void testFunc(){ }
};

 

 这里说明了一个问题,this指针一定是不存储在对象中,而是在栈空间中~。

4. 扩展:

定义一个计算成员变量在类中偏移量的宏

#define OFFSETOF(TYPE, ELEM) ((int)(&(((TYPE*)0)->ELEM)))
struct testClass
{int _i;char _c;double _d;void testFunc(){ }
};

做法:

比如,_c 是的地址是0x00000004,它的偏移量正好就是4~

疑问:

((TYPE*)0)->ELEM 不会野指针吗~?答案是不会的,现代的编译器十分智能,它如果检测到((TYPE*)0)->ELEM 之前还有一个&取地址符号,那么他就不会去访问这块空间,而仅仅是拿到他的地址。如果只写((TYPE*)0)->ELEM ,那么必然是会野指针的~~!

http://www.dtcms.com/a/291029.html

相关文章:

  • 基于 HAProxy 搭建 EMQ X 集群
  • vscode创建vue项目报错
  • 如何判断自己的电脑或主机是否支持DDR5内存?
  • Android 默认图库播放视频没有自动循环功能,如何添加2
  • MVC模式
  • vcruntime140_1.dll文件丢失?终极修复指南:从错误分析到修复全流程
  • Ubuntu 22.04 使用 Docker 安装 Redis 5 (安装包形式)
  • linux定时器使用
  • AD域控制器虚拟化的安全加固最佳实践
  • 从IR到DS的转化过程中,如何确保各阶段需求不偏离用户原始场景?有哪些验证方法?
  • 吴恩达 机器学习cs229-学习笔记-更新中
  • 动静态库原理与实战详解
  • Linux 721 创建实现镜像的逻辑卷
  • 网站域名备案和服务器有关系吗
  • 《电⼦元器件零基础⼊⻔》
  • 汽车售后诊断仪DoIP和CANBus诊断指令分析
  • Linux的磁盘存储管理实操——(中)——逻辑卷管理实战
  • Ubuntu 22.04编译安装Nginx 1.28
  • Docker实践:使用Docker部署blog轻量级博客系统
  • 我的NAS进化史:用1Panel和内网穿透把零配件变成远程中枢
  • 超详细解析:Java装箱与拆箱(附完整数据类型清单)
  • 在 HTTP GET 请求中传递参数有两种标准方式
  • 【Java】Spring的依赖注入理解,@Autowired用法
  • 网络数据分层封装与解封过程的详细说明
  • STM32 开发的鼠标:技术详解与实现指南
  • RBAC(Role-Based Access Control,基于角色的访问控制)介绍(一种通过角色来管理用户权限的访问控制模型)
  • 深入解析 SymPy 中的符号计算:导数与变量替换的实践指南
  • 【未限制消息消费导致数据库CPU告警问题排查及解决方案】
  • 司南评测体系全新升级,“五位一体”评估全链路关键能力
  • 神经网络过拟合处理:原理与实践