(Boolan) C++ 类型大小和内存分布(虚函数指针、虚表、内存对齐问题)

本文主要是介绍(Boolan) C++ 类型大小和内存分布(虚函数指针、虚表、内存对齐问题),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!


题目要求

回答:

(以下大部分都是基于x64编译器下的windows平台的gcc version 5.3.0 (GCC)编译器的测试结果,不能其他平台也能得出完全一致的结论,如果在x32下编译结果会指出)
由于class相较于struct,默认的成员就是private,代码中没有特地强调private

  • Fruit的类型大小为32,Apple的类型大小为40。

    • 完整测试用代码:
      http://rextester.com/AUJV82101
      • 点击上方连接可以进入全套代码,点击左下角的“run”按钮可以查看运行后的结果。
      • 说明:
        • 程序所有的对象均创建在栈中,由系统自动管理,无需手动释放内存
  • 图示:


Fruit类型的大小所占的内存(x64编译器下的结构) 4 * 8 = 32 Byte

注:虚函数指针因为是一个指针,其大小应该为4个字节,但在此我想说,如果使用x64编译器生成的64位程序的指针大小为8个字节。(一个只含有虚函数的struct,x64编译旗下,虚函数指针为8字节;x86编译器上虚函数指针和普通指针没啥区别,都是4个字节)。
在后续我有详细的测试论证过程。


Fruit类型的大小所占的内存(x86编译器下的结构) 4 * 8 = 32 Byte

Apple类型的大小所占的内存(x64编译器下的结构) 5 * 8 = 40 Byte

Apple类型的大小所占的内存(x86编译器下的结构) 5 * 8 = 40 Byte

关于答案以下是非常详细的测试和推理,篇幅较长,感谢您阅读,希望您多多指正。

答案分析:

  • 完整测试用代码:
    http://rextester.com/AUJV82101
    • 点击上方连接可以进入全套代码,点击左下角的“run”按钮可以查看运行后的结果。
    • 代码运行的初级结论

      代码初级结论(x64编译器的结果)
- Fruit类和Apple类的相关定义class Fruit {int no;double weight;char key;public:void print() {   }virtual void process() {   }};class Apple : public Fruit {int size;char type;public:void save() {   }virtual void process() {   }};
  • 提出疑问
    1 对于Fruit类来说,成员由int、double和char组成,其中,不难由程序员算结果可知sizeof(int) = 4、sizeof(double) = 8、sizeof(char) = 1,那么1+4+8=13,为何sizeof(Fruit)的结果为32?
    2 对于Apple来说,成员有int、char组成,其中,不难由程序得知sizeof(int) = 4、sizeof(char) = 1,那么1 + 4 = 5,为何sizeof(Apple)的结果为40呢?
    3 这样定义是否合理,是否存在着内存的浪费?
    4 内存中的额外空间用做了什么?这些空间是否有规律可循?他们是什么?都占多大的内存空间?
    ......

  • 分析:

    为了弄清楚这些疑问,需要准备一系列的代码来做实验。

    • 首先我们先来验证最基础的一个特点就是内存对齐的问题。
      • 什么是内存对齐。内存对齐是 编译器 层面管理的问题,是编译器管理数据位置的一种组织方式。
      • 对齐系数。其实可以把他理解为编译器的来存放内存时,划分内存空间的一把“尺子”。通过这个尺子来量出该怎么划分内存空间。也可以把它理解为切内存——这块蛋糕,所用的最小单位。如果被选中了相应的对其系数,那么,也就决定了存放数据的内存单元的每一行有多宽,所以得出来的内存空间大小,一定是对其系数的倍数!
        • 那么如何来得到对齐系数呢?

          方式一:
          程序员可以通过预编译命令#pragma pack(n),n=1,2,4,8,16来改变这一系数,其中的n就是你要指定的“对齐系数”。

          方式二:
          由编译器自信决定。对于我这次测试的平台来说,这个编译器的规则为,采用成员中最长的变量的长度作为对其系数

        • 既然知道了对齐系数,那么是否可以帮助解释之前提出的疑问呢?!
          答案是,可以解释部分内容,想要全部弄明白还得等等,我们先来看看这块能解释多少吧。
          如果这时候那Fruit为例来看,它其中的成员有int,double和char所组成,这三个变量中,最长的应该是double了。所以Fruit的大小一定是sizeof(double)的倍数,也就是8的倍数。目前看,Fruit的大小为32,是符合这个观点的。那么这三个成员是如何排列呢?
          其实他们的安排顺序还是狠简单粗暴的,就是定义变量的顺序来组织他们在内存中的位置。
          比如,Fruit的成员定义顺序是int,double,char,则编译器会先将int按照,对齐系数放入内存中,再看后面的变量,如果,两者相加小于对齐系数,则放在同一行,如果大于,就单独再开一行。那么,Fruit的对齐系数为double的8,sizeof(int)+sizeof(double)

这篇关于(Boolan) C++ 类型大小和内存分布(虚函数指针、虚表、内存对齐问题)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/914222

相关文章

NameNode内存生产配置

Hadoop2.x 系列,配置 NameNode 内存 NameNode 内存默认 2000m ,如果服务器内存 4G , NameNode 内存可以配置 3g 。在 hadoop-env.sh 文件中配置如下。 HADOOP_NAMENODE_OPTS=-Xmx3072m Hadoop3.x 系列,配置 Nam

好题——hdu2522(小数问题:求1/n的第一个循环节)

好喜欢这题,第一次做小数问题,一开始真心没思路,然后参考了网上的一些资料。 知识点***********************************无限不循环小数即无理数,不能写作两整数之比*****************************(一开始没想到,小学没学好) 此题1/n肯定是一个有限循环小数,了解这些后就能做此题了。 按照除法的机制,用一个函数表示出来就可以了,代码如下

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

【C++ Primer Plus习题】13.4

大家好,这里是国中之林! ❥前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。有兴趣的可以点点进去看看← 问题: 解答: main.cpp #include <iostream>#include "port.h"int main() {Port p1;Port p2("Abc", "Bcc", 30);std::cout <<

C++包装器

包装器 在 C++ 中,“包装器”通常指的是一种设计模式或编程技巧,用于封装其他代码或对象,使其更易于使用、管理或扩展。包装器的概念在编程中非常普遍,可以用于函数、类、库等多个方面。下面是几个常见的 “包装器” 类型: 1. 函数包装器 函数包装器用于封装一个或多个函数,使其接口更统一或更便于调用。例如,std::function 是一个通用的函数包装器,它可以存储任意可调用对象(函数、函数

C++11第三弹:lambda表达式 | 新的类功能 | 模板的可变参数

🌈个人主页: 南桥几晴秋 🌈C++专栏: 南桥谈C++ 🌈C语言专栏: C语言学习系列 🌈Linux学习专栏: 南桥谈Linux 🌈数据结构学习专栏: 数据结构杂谈 🌈数据库学习专栏: 南桥谈MySQL 🌈Qt学习专栏: 南桥谈Qt 🌈菜鸡代码练习: 练习随想记录 🌈git学习: 南桥谈Git 🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈�

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

06 C++Lambda表达式

lambda表达式的定义 没有显式模版形参的lambda表达式 [捕获] 前属性 (形参列表) 说明符 异常 后属性 尾随类型 约束 {函数体} 有显式模版形参的lambda表达式 [捕获] <模版形参> 模版约束 前属性 (形参列表) 说明符 异常 后属性 尾随类型 约束 {函数体} 含义 捕获:包含零个或者多个捕获符的逗号分隔列表 模板形参:用于泛型lambda提供个模板形参的名

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

购买磨轮平衡机时应该注意什么问题和技巧

在购买磨轮平衡机时,您应该注意以下几个关键点: 平衡精度 平衡精度是衡量平衡机性能的核心指标,直接影响到不平衡量的检测与校准的准确性,从而决定磨轮的振动和噪声水平。高精度的平衡机能显著减少振动和噪声,提高磨削加工的精度。 转速范围 宽广的转速范围意味着平衡机能够处理更多种类的磨轮,适应不同的工作条件和规格要求。 振动监测能力 振动监测能力是评估平衡机性能的重要因素。通过传感器实时监