C++对象模型与内存位对齐的简单分析(GNU GCC&VS2015编译器)

以Fruit和Apple为例进行分析:

Fruit和Apple的定义如下:

通过在两种编译环境下的测试(GNU GCC & VS2015),可以发现这两种编译器的对象模型是一样的,如下图所示:

Apple是Fruit的子类,此为两级的单链继承结构。在Apple和Fruit对象内部,均遵循以下原则:

  1. 对象中的第一个成员是指向虚表的虚指针;
  2. 对象是按照声明中的顺序被保存的;

然而,两种编译器的内存的位对齐方式略有不同。

对于GNU GCC编译器而言,其遵循以下的原则:

  1. 按声明中出现的顺序进行内存分配
  2. 变量的起始偏移地址必须是自己大小的倍数,例如在struct{bool a;double b;}中,a占1个字节,然而b的大小是8个字节,因此b必须从编译地址为8的位置看是,也就是说,a和b要空7个字节。其之所以这样设计,是因为在读取一个变量时,这种方式读取可以使cache速度更快。
  3. 如果类中存在虚函数,在对象的起始处会有虚指针。
  4. 在所有变量的内存分配结束后,对象要填补成内存中的最大的基本类型变量的倍数。例如,如果一个类中最大的基本类型是double,那么它最后需要填补成8的整数倍。

还有三个特点在Fruit和Apple的关系中没有涉及到,他们是:

  1. 多重继承的情况下,在每个基类的前边上会有不同的vptr;
  2. 如果在派生类中存在新的虚函数,则会产生一个兼容基类的虚表,而不会添加新的表;
  3. 组合关系时,内部类的起始地址应从内部类的最大的基本数据类型的整数倍处开始。

综合前4个特点,可以计算得到在GNU GCC的编译环境下,Fruit的大小是(4+4+8+(1+7))=24Bytes; Apple的大小是(24+4+(1+3))=32Bytes,如下图:

然而,在VS2015的编译环境下,虚指针位对齐的方式是不同的。VS2015中要求数据成员的起始地址也必须是内部最大基本数据类型的整数倍,也就是说,在虚指针和数据成员之间必须存在4个占位字节,因此Fruit的大小是((4+4)+(4+4)+8+(1+7))=32Bytes;而Apple的大小是(32+4+(1+4))=40;

如图所示,尽管指针变量的大小为4字节,no的偏移量仍然是从8开始的。

Apple的内存截图如下:

前32个字节:

后8个字节:

其中,0x649bfb00位虚指针地址,0xcc为占位符,第一个0xffffffff为no,0x0000000000000000为weight,0x01为key。

注:如果将double变量删去,则第一个数据成员从4开始,也就是说Fruit的大小应该变为(4+4+(1+3))=12,如图:

内存分配图如下:

这验证了VS2015编译器的额外条件。

时间: 2024-10-01 21:10:25

C++对象模型与内存位对齐的简单分析(GNU GCC&VS2015编译器)的相关文章

C语言精要总结-内存地址对齐与struct大小判断篇

在笔试时,经常会遇到结构体大小的问题,实际就是在考内存地址对齐.在实际开发中,如果一个结构体会在内存中高频地分配创建,那么掌握内存地址对齐规则,通过简单地自定义对齐方式,或者调整结构体成员的顺序,可以有效地减少内存使用.另外,一些不用边界对齐.可以在任何地址(包括奇数地址)引用任何数据类型的的机器,不在本文讨论范围之内. 什么是地址对齐 计算机读取或者写入存储器地址时,一般以字(因系统而异,32位系统为4个字节)大小(N)的块来执行操作.数据对齐就是将数据存储区的首地址对齐字大小(N)的某个整数

windows和Linux内存的对齐方式

一.内存对齐的初步讲解 内存对齐可以用一句话来概括: "数据项只能存储在地址是数据项大小的整数倍的内存位置上" 例如int类型占用4个字节,地址只能在0,4,8等位置上. 例1: #include <stdio.h> struct xx{ char b; int a; int c; char d; }; int main() { struct xx bb; printf("&a = %p\n", &bb.a); printf("

嵌入式Linux C语言(六)——内存字节对齐

嵌入式Linux C语言(六)--内存字节对齐 一.内存字节对齐简介 1.内存字节对齐 计算机中内存空间都是按照字节划分的,从理论上讲对任何类型的变量的访问可以从任何地址开始,但是在程序实际编译过程中,编译器会对数据类型在编译过程中进行优化对齐,编译器会将各种类型数据按照一定的规则在空间上排列,而不是顺序的排放,这就是内存字节对齐. 2.内存字节对齐原因 不同硬件平台对存储空间的处理是不同的.一些平台对某些特定类型的数据只能从某些特定地址开始存取.比如某些架构的CPU在访问一个没有进行对齐的变量

FFmpeg源代码简单分析:内存的分配和释放

本文简单记录一下FFmpeg中内存操作的函数. 内存操作的常见函数位于libavutil\mem.c中.本文记录FFmpeg开发中最常使用的几个函数:av_malloc(),av_realloc(),av_mallocz(),av_calloc(),av_free(),av_freep(). av_malloc() av_malloc()是FFmpeg中最常见的内存分配函数.它的定义如下. #define FF_MEMORY_POISON 0x2a #define ALIGN (HAVE_AVX

malloc分配内存进行对齐的操作

昨天面试高通Linux Kernel,面试官考了一个malloc内存对齐的问题,我晚上的时候细细的想了一下,实在是学习的不到位. 有的时候真的应该感谢,像是Qt.Ubuntu Gcc的编译器,他们做的工作很多,malloc直接分配对齐了的内存.如果真的是一个 不是很完善的平台,直接分配给你一个没对齐的内存,当我们传输字节指令的时候分高低八位的时候,定义联合体和结构体这样的偷懒方式 绝对会发一个错误的指令. oh 要学的太多了.... 实际上,对齐参数(MALLOC_ALIGNMENT)大小的设定

JAVA 通过位运算进行简单的加密

我们可以通过一个简单的位运算进行简单的加密 import java.util.Scanner; public class Example{ public static void main(String[]args){ Scanner input = new Scanner(System.in); System.out.println("请输入一个英文字符或解密字符串"); //获取用户输入的字符 String password = scan.nextLine(); //使用String

windows下开发服务器 位对齐问题

windows下开发服务器 位对齐问题 问题描述: 我在开发手机客服端大厅时,发送一个字节长度为奇数(2N-1)的数据,服务器是老是拒绝数据,长度不对,而远行PC端大厅,断点调试发现同样的数据却是偶数(2N)? 解决办法: windows默认是 #pragma pack(8) 我们开发可能看到是#pragma pack(1),把服务器改成#pragma pack(1) 版权声明:本文为博主原创文章,未经博主允许不得转载.

FFmpeg的HEVC解码器源代码简单分析:解析器(Parser)部分

上篇文章概述了FFmpeg中HEVC(H.265)解码器的结构:从这篇文章开始,具体研究HEVC解码器的源代码.本文分析HEVC解码器中解析器(Parser)部分的源代码.这部分的代码用于分割HEVC的NALU,并且解析SPS.PPS.SEI等信息.解析HEVC码流(对应AVCodecParser结构体中的函数)和解码HEVC码流(对应AVCodec结构体中的函数)的时候都会调用该部分的代码完成相应的功能. 函数调用关系图 FFmpeg HEVC解析器(Parser)部分在整个HEVC解码器中的

u-boot分析(十一)----MMU简单分析|u-boot分析大结局|学习规划

u-boot分析(十一) 通过前面十篇博文,我们已经完成了对BL1阶段的分析,通过这些分析相信我们对u-boot已经有了一个比较深入的认识,在BL2阶段大部分是对外设的初始化,并且有的我们已经分析过,在这篇博文我打算对BL1阶段没有分析到的重要外设进行简单分析,并结束对u-boot的分析,同时对后面自己的博文进行简单的规划,希望有兴趣的朋友跟我一块学习和研究嵌入式. 今天我们会分析到以下内容: 1.      MMU分析(内容出自我以前的博客) 2.      裸机开发总结 3.      后期