3
点赞
0
评论
0
转载
收藏

你所不知道的C/C++那些事(二)诡异的对象大小

众所周知C++在底层背着程序员做了很多不为人知的隐秘工作,如果我们不了解其中的一些替换,则很可能出现结果跟我们料想的结果很不一致的情况。

下面我们就讨论一下我们亲手所构造出的对象的大小可能出乎你意料的情况:

说明: 编译器:gcc version 4.8.1     系统环境:64位opensuse 13.1


情况1: 空对象大小

我们用以下代码:

#include <iostream>
using namespace std ;
 
class A
{};
 
int main()
{
    A a ;
    cout << "sizeof a is : " << sizeof(a) << endl ;
    return 0 ;
}

构造一个空类A并实例化一个a对象,求出其大小:

选区_002.png

大小为1个字节,那这一个字节怎么来的呢?

原因:编译器会在空类中安插进去一下char,目的是为了让空类实例化出来的第一个对象在内存中都有自己独特的地址,比如下面的情况:

    A a ;
    A b ;
     
    if(&a == &b)
    {
        cout << "addr of a equal to addr of b" << endl ;
    }

a的地址和b的地址不相等并且相差一个字节,正是编译器安插进来的1字节。


情况2:考虑Alignment时的对象大小

有两个对象,留意它们内部只是数据的声明顺序不一样:

class A
{
    public:
    int int_1 ;
    short short_1 ;
    char char_1 ;
};
 
class B
{
    public:
    char char_1 ;
    int int_1 ;
    short short_1 ;
};

然后我们看看它们的大小:

    cout << "sizeof int is : " << sizeof(int) << endl ;
    cout << "sizeof short is : " << sizeof(short) << endl ;
    cout << "sizeof char is : " << sizeof(char) << endl ;
    
    A a ;
    B b ;
    cout << "sizeof a is : " << sizeof(a) << endl ;
    cout << "sizeof b is : " << sizeof(b) << endl ;

结果可能会让你惊讶:

选区_003.png

三个成员变量加起来理应是7个字节,但是a对象却有8个字节,b对象甚至达到12个字节。


这里就要考虑到 Data alignment(数据对齐)这个情况,wikipedia这样解说这个术语:

Data structure alignment is the way data is arranged and accessed in computer memory. It consists of two separate but related issues: data alignment and data structure padding. Data alignment means putting the data at a memory address equal to some multiple of the word size, which increases the system's performance due to the way the CPU handles memory. To align the data, it may be necessary to insert some meaningless bytes between the end of the last data structure and the start of the next, which is data structure padding.

https://en.m.wikipedia.org/wiki/Data_structure_alignment#


MSDN 写道
Alignment is a property of a memory address, expressed as the numeric address modulo a power of 2. For example, the address 0x0001103F modulo 4 is 3; that address is said to be aligned to 4n+3, where 4 indicates the chosen power of 2. The alignment of an address depends on the chosen power of two. The same address modulo 8 is 7.
An address is said to be aligned to X if its alignment is Xn+0.
CPUs execute instructions that operate on data stored in memory, and the data are identified by their addresses in memory.


  • Data alignment(数据对齐)

简单说就是为了CPU的读取效率而把不同大小的数据安排在有规律的内存地址上而定的一种属性,比如我这里的 int 的alignment 为4,而short 的为2,char的为1。而这些变量所在的内存起始地址要是它们的 alignment 的整数倍。

  • Data structure padding

在 class 的所有 member 的 alignment 中,找到 alignment 的最大值,在class的最后一个member填补 padding bytes使整个class的size 为此aligment 的整数倍。

以上两个概念详细请参考:http://acoder.cc/Article/view/aid/424.html


有了以上两个概念,我们就可以来讨论对象a 和 b 的数据模型了:

选区_006.png

对象a和b里最大的alignment为int的4个字节,由于声明顺序不一样,当安排好了所有的数据后,a为了达到最大alignment的整数倍,要填充一个字节的padding(红色字样处),而b则需要填充两个字节的padding(红色字样处)。

结果就出来了a是8个字节而b是12字节。


情况3:带有虚函数的对象大小

看看这个例子:

class A
{
    public:
        A(){}
        virtual ~A(){}
         
        int int_1 ;
        short short_1 ;
        char char_1 ;
};

我们顺带计算一下这个平台环境下指针的大小:

选区_007.png

指针大小是8字节,这个对象a与情况二的对象a只是多了一个虚析构函数。但是其大小增加了8字节。

原 因:熟悉C++的都知道C++的多态实现是依靠虚函数及在运行时使用pointer 或者 reference 来间接调用虚函数,而C++会为每个带有虚函数的类分配一个虚函数表,表中第一项记录所有该类的每个虚函数的指针,这个表是这个类所有实例化的对象共享 的,这个类的所有对象上会被编译器安插一个指针指向这个共享的虚函数表。(关于虚函数表详细讲解请看这里:http://acoder.cc/Article/view/aid/435.html

就是说 a 对象的内存开始位置会有一个被编译器自动安排插入的指针存在,而我们顺带求出的这个环境下指针是占用8个字节的内存的,而这个a对象在情况下求得8个字节大小加上编译器自动安插的指针,则刚好是16字节。


这是一个系列文章:第一篇请看这里: http://acoder.cc/Article/view/aid/280.html

欢迎关注ACoder技术社区:http://acoder.cc 

关 于这些C++的内容为作者阅读《尝试探索C++对象模型》《C++ Primer》《Effective C++》理解所得,内容如有不当之处,请联系w@programfish.com更正,转载请注明来自“ACoder社区”及原文地 址:http://acoder.cc/Article/view/aid/436.html 


声明:本内容系学者网用户个人学术动态分享,不代表平台立场。

华南师范大学
近期热门动态
课堂里学不到的C与C++那些事(一)
2965 2015-01-30 16:03:58
论docker中 CMD 与 ENTRYPOINT 的区别
2896 2014-11-16 20:51:32
为什么TCP连接需要三次握手断开需要四次握手
1693 2015-07-22 14:54:18
#
731 2014-11-15 14:10:42
SCHOLAT.com 学者网
免责声明 | 关于我们 | 联系我们
联系我们:
返回顶部