c++中类对象的内存对齐

很多C++书籍中都介绍过,一个Class对象需要占用多大的内存空间。最权威的结论是:

*非静态成员变量总合。(not static)

*加上编译器为了CPU计算,作出的数据对齐处理。(c语言中面试中经常会碰到内存对齐的问题)

*加上为了支持虚函数(virtual function),产生的额外负担。

下面给出几个程序来看一下:

#include <iostream>
#include <cstdio>
#include <string>
using namespace std;
class Car1{
};

void fun1(void)
{
    int size =0;
    Car1 objCar;
    size = sizeof(objCar);
    printf("%s is %d\n","Class Car1 Size",size);
}

class Car2{
private:
    int nLength;
    int nWidth;
};
void fun2(void)
{
    int size = 0;
    Car2 objCar;
    size = sizeof(objCar);
    printf("%s is %d\n","Class Car2 Size",size);
}

class Car3{
private:
    int nLength;
    int nWidth;
    static int sHight;
};
void fun3(void)
{
    int size =0;
    Car3 objCar;
    size =sizeof(objCar);
    printf("%s is %d\n","Class Car3 Size",size);
}

class Car4{
private:
    char chLogo;
    int nLength;
    int nWidth;
    static int sHigh;
};
void fun4(void)
{
    int size =0;
    Car4 objCar;
    size = sizeof(objCar);
    printf("%s is %d\n","Class Car4 Size",size);
}

class Car5{
public:
    Car5(){};
    ~Car5(){};
public:
    void Fun(){};
};

void fun5(void)
{
    int size =0 ;
    Car5 objCar;
    size = sizeof(objCar);
    printf("%s is %d\n","Class Car5 Size",size);
}

class Car6{
public:
    Car6(){};
    ~Car6(){};
public:
    void Fun(){};
private:
    int nLength;
    int nWidth;
};
void fun6(void)
{
    int size = 0;
    Car6 objCar;
    size = sizeof(objCar);
    printf("%s is %d\n","Class Car6 Size",size);
}

class Car7{
public:
    Car7(){};
    virtual ~Car7(){};
public:
    void Fun(){};
};
void fun7(void)
{
    int size = 0;
    Car7 objCar;
    size = sizeof(objCar);
    printf("%s is %d\n","Class Car7 Size",size);
}

class Car8{
public:
    Car8(){};
    virtual ~Car8(){};
public:
    void Fun(){};
    virtual void Fun1(){}
};
void fun8(void)
{
    int size = 0;
    Car8 objCar;
    size = sizeof(objCar);
    printf("%s is %d\n","Class Car8 Size",size);
}

int main()
{
    fun1();
    fun2();
    fun3();
    fun4();
    fun5();
    fun6();
    fun7();
    fun8();
}

编译:g++ memAlign.cpp -o memAlign

输出结果:

Class Car1 Size is 1
Class Car2 Size is 8
Class Car3 Size is 8
Class Car4 Size is 12
Class Car5 Size is 1
Class Car6 Size is 8
Class Car7 Size is 8Class Car8 Size is 8

ps:上述编译环境是在mac os下的g++

下面我们具体来分析一下这几个情况:

1、空类、单一继承的空类、多重继承的空类所占空间大小为:1(字节,下同);

2、一个类中,虚函数本身、成员函数(包括静态与非静态)和静态数据成员都是不占用类对象的存储空间的;

3、因此一个对象的大小≥所有非静态成员大小的总和;

4、当类中声明了虚函数(不管是1个还是多个),那么在实例化对象时,编译器会自动在对象里安插一个指针vPtr指向虚函数表VTable;

5、虚继承的情况:由于涉及到虚函数表和虚基表,会同时增加一个(多重虚继承下对应多个)vfPtr(virtual function table)指针指向虚函数表vfTable和一个vbPtr(virtual base pointer)指针指向虚基表vbTable,这两者所占的空间大小为:8(或8乘以多继承时父类的个数);

6、在考虑以上内容所占空间的大小时,还要注意编译器下的“补齐”padding的影响,即编译器会插入多余的字节补齐;(请参考《c和指针》)

7、类对象的大小=各非静态数据成员(包括父类的非静态数据成员但都不包括所有的成员函数)的总和+ vfptr指针(多继承下可能不止一个)+vbptr指针(多继承下可能不止一个)+编译器额外增加的字节。

参考:C++虚函数工作原理和(虚)继承类的内存占用大小计算

在VC 6.0中,结果是

Class Car1 Size is 1
Class Car2 Size is 8
Class Car3 Size is 8
Class Car4 Size is 12
Class Car5 Size is 1
Class Car6 Size is 8
Class Car7 Size is 4
Class Car8 Size is 4

主要的不同点是:在Car7和Car8,在VC 6.0中虚函数指针占用4个字节,在gcc编译器中占用8个字节。

        也可以换一种说法是virtual函数指针在VC下以4字节对齐,在gcc下是8字节对齐,这样解释就比较清楚了。

二、编程实现成员在类或结构体中的偏移量

代码如下所示:

 1 #include <cstdio>
 2 #include <iostream>
 3 #define pos(type,member) (&((type *)0)->member)
 4
 5 class car{
 6 public:
 7     car(){}
 8     ~car(){}
 9 public:
10     virtual void fun(){}
11 private:
12     int c;
13 public:
14     void print()
15     {
16         printf("%x\n",pos(car,c));
17     }
18 };
19
20 int main()
21 {
22     struct Node{
23         int a ;
24         char b;
25         int c;
26     };
27     car objCar;
28 //    printf("%x\n",&((struct Node *)0)->b);
29     printf("%x\n",pos(struct Node,b));
30     printf("%x\n",pos(struct Node,c));
31 //    printf("%x\n",pos(class car,c));
32     objCar.print();
33     return 0;
34 }

其中关键的是找到函数能够实现计算成员在类中的偏移量,这里用了宏来实现的。

#define pos(type,member) (&((type *)0)->member)

(从地址0开始的一个type结构体或者类,其成员的地址就是成员所在类或结构体的偏移量)

上述程序的输出结果就是:4 8 8

时间: 2024-10-26 12:24:36

c++中类对象的内存对齐的相关文章

NumPy-快速处理数据--ndarray对象--多维数组的存取、结构体数组存取、内存对齐、Numpy内存结构

本文摘自<用Python做科学计算>,版权归原作者所有. 上一篇讲到:NumPy-快速处理数据--ndarray对象--数组的创建和存取 接下来接着介绍多维数组的存取.结构体数组存取.内存对齐.Numpy内存结构 一.多维数组的存取 多维数组的存取和一维数组类似,因为多维数组有多个轴,因此它的下标需要用多个值来表示,NumPy采用组元(tuple)作为数组的下标.如二维数组需要(x, y)的元组标记一个数组元素:三维数组需要(x, y, z)的元组标记一个元素. 如下图所示,a为一个6x6的二

Java对象的内存布局

Java对象的内存布局:对象头(Header),实例数据(Instance Data),对齐填充(Padding):另外:不同的环境结果可能有差异,我所在的环境是HotSpot虚拟机,64位Windows. 对象头 对象头在32位系统上占用8bytes,64位系统上占用16bytes. System.out.println("sizeOf(new Object()) = " + sizeOf(new Object())); sizeOf(new Object()) = 16 实例数据

关于内存对齐的那些事

Wrote by mutouyun. (http://darkc.at/about-data-structure-alignment/) 1. 内存对齐(Data Structure Alignment)是什么 内存对齐,或者说字节对齐,是一个数据类型所能存放的内存地址的属性(Alignment is a property of a memory address). 这个属性是一个无符号整数,并且这个整数必须是2的N次方(1.2.4.8.--.1024.--). 当我们说,一个数据类型的内存对齐

struct内存对齐1:gcc与VC的差别

struct内存对齐:gcc与VC的差别 内存对齐是编译器为了便于CPU快速访问而采用的一项技术,对于不同的编译器有不同的处理方法. Win32平台下的微软VC编译器在默认情况下采用如下的对齐规则: 任何基本数据类型T的对齐模数就是T的大小,即sizeof(T).比如对于double类型(8字节),就要求该类型数据的地址总是8的倍数,而char类型数据(1字节)则可以从任何一个地址开始.Linux下的GCC奉行的是另外一套规则:任何2字节大小(包括单字节吗?)的数据类型(比如short)的对齐模

C++ 对象的内存布局(上)

C++ 对象的内存布局(上) 陈皓 http://blog.csdn.net/haoel 点击这里查看下篇>>> 前言 07年12月,我写了一篇<C++虚函数表解析>的文章,引起了大家的兴趣.有很多朋友对我的文章留了言,有鼓励我的,有批评我的,还有很多问问题的.我在这里一并对大家的留言表示感谢.这也是我为什么再写一篇续言的原因.因为,在上一篇文章中,我用了的示例都是非常简单的,主要是为了说明一些机理上的问题,也是为了图一些表达上方便和简单.不想,这篇文章成为了打开C++对象模

java对象的内存布局(一):计算java对象占用的内存空间以及java object layout工具的使用

最近在学习java对象内存布局方面的一些知识,主要是想知道一个java对象到底占用多少内存空间,以及java对象在内存中到底是什么样子的.c/c++中的sizeof运算符能够方便地告诉我们一个变量占用的内存空间,但是在java中却没有直接提供这种机制.如果想获取java对象占用的内存大小,可以利用java的Instrumentation机制.java.lang.instrument.Instrumentation这个接口提供了getObjectSize(Object objectToSize),

源码分析:Java对象的内存分配

Java对象的分配,根据其过程,将其分为快速分配和慢速分配两种形式,其中快速分配使用无锁的指针碰撞技术在新生代的Eden区上进行分配,而慢速分配根据堆的实现方式.GC的实现方式.代的实现方式不同而具有不同的分配调用层次. 下面就以bytecodeInterpreter解释器对于new指令的解释出发,分析实例对象的内存分配过程: 一.快速分配 1.实例的创建首先需要知道该类型是否被加载和正确解析,根据字节码所指定的CONSTANT_Class_info常量池索引,获取对象的类型信息并调用is_un

C语言 结构体的内存对齐问题与位域

http://blog.csdn.net/xing_hao/article/details/6678048 一.内存对齐 许多计算机系统对基本类型数据在内存中存放的位置有限制,它们会要求这些数据的首地址的值是某个数k(通常它为4或8)的倍数,这就是所谓的内存对 齐,而这个k则被称为该数据类型的对齐模数(alignment modulus).当一种类型S的对齐模数与另一种类型T的对齐模数的比值是大于1的整数,我们就称类型S的对齐要求比T强(严格),而称T比S弱(宽 松).这种强制的要求一来简化了处

C++ 对象的内存布局(上)

转自陈皓的博客 前言 在谈论虚函数表里,至少有以下这些内容没有涉及: - 有成员变量的情况. - 有重复继承的情况. - 有虚拟继承的情况. - 有钻石型虚拟继承的情况. 所以,这篇文章将会是<C++虚函数表解析>的一个续篇,也是一篇高级进阶的文章. 对象的影响因素 简而言之,我们一个类可能会有如下的影响因素: 成员变量 虚函数(产生虚函数表) 单一继承(只继承于一个类) 多重继承(继承多个类) 重复继承(继承的多个父类中其父类有相同的超类) 虚拟继承(使用virtual方式继承,为了保证继承