一些关于sizeof的例子

来源:互联网 发布:服装网络供货 编辑:程序博客网 时间:2024/05/16 15:27

转自:http://zhengrongyang.spaces.live.com/blog/

对几组sizeof信息的分析

对于很多C++新手而言,对象或变量的sizeof信息总是让人捉摸不透,以下程序列举了几个典型的sizeof信息,希望能解答大家在使用sizeof时的疑问。
在列举这几个例子前需要说明以下几点:
1
、在Win32平台上,指针长度都是4字节,char*int*double*如此,vbptr(virtual base table pointer)、vfptr(virtual function table pointer)也是如此;
2
、对于结构体(或类),编译器会自动进行成员变量的对齐,以提高运算效率。自然对齐(natural alignment)也称默认对齐方式是按结构体的成员中size最大的成员对齐的,强制指定大于自然对齐大小的对齐方式是不起作用的。
3
、不推荐强制对齐,大量使用强制对齐会严重影响处理器的处理效率。

范例1(一个简单的C语言的例子)
void
 f(int arr[])
{

    cout << "sizeof(arr) = " << sizeof(arr) << endl; //当被作为参数进行传递时,数组失去了其大小信息
}

void
 main()
{

    char
 szBuf[] = "abc";
    cout << "sizeof(szBuf) = " << sizeof(szBuf) << endl; //输出数组占用空间大小

    char
* pszBuf = szBuf;
    cout << "sizeof(pszBuf) = " << sizeof(pszBuf) << endl; //输出的是指针的大小

    int
 iarr[3]; iarr;
    cout << "sizeof(iarr) = " << sizeof(iarr) << endl; //输出数组占用空间大小
    f(iarr);

    int
* piarr = iarr;
    cout << "sizeof(piarr) = " << sizeof(piarr) << endl; //输出指针的大小
}

范例2(一个涉及alignment的例子)
struct
 DATA1
{

    char
    c1; //偏移量0,累积size = 1
    char    c2; //偏移量1,累积size = 1 + 1 = 2
    short    si; //偏移量2,累积size = 2 + 2
};

struct
 DATA2
{

    char
    c1; //偏移量0,累积size = 1
    short    si; //偏移量1 + (1),累积size = 1 + (1) + 2 = 4
    char    c2; //偏移量4,累积size = 4 + 1 = 5,但按最大长度sizeof(short) = 2对齐,故最后取6
};

struct
 DATA3
{

    char
    c1; //偏移量0,累积size = 1
    double    d; //偏移量1 + (7),累积size = 1 + (7) + 8 = 16
    char    c2; //偏移量16,累积size = 16 + 1 = 17,但按最大长度sizeof(double) = 8对齐,故最后取24
};

#pragma pack(push,1) //强制1字节对齐
struct DATA4
{

    char
    c1; //偏移量0,累积size = 1
    double    d; //偏移量1,累积size = 1 + 8 = 9
    char    c2; //偏移量9,累积size = 9 + 1 = 10
};
#pragma pack(pop) //恢复默认对齐方式

struct
 DATA5
{

    char
    c1;
    double
    d;
    char
    c2;
};


void
 main()
{

    cout << "sizeof(DATA1) = " << sizeof(DATA1) << endl;
    cout << "sizeof(DATA2) = " << sizeof(DATA2) << endl;
    cout << "sizeof(DATA3) = " << sizeof(DATA3) << endl;
    cout << "sizeof(DATA4) = " << sizeof(DATA4) << endl;
    cout << "sizeof(DATA5) = " << sizeof(DATA5) << endl;
}


范例3(C++语言特征对sizeof的影响)
class
 CA
{
};


class
 CB : public CA
{

public
:
    void
 func() {}
};


class
 CC : virtual public CA
{
};


class
 CD
{

    int
 k; //私有成员
public:
    CD() {k = -1;}
    void
 printk() { cout << "k = " << k << endl; }
};


class
 CE : public CD
{
};


class
 CF
{

    virtual
 void func() {}
};


void
 main()
{

    cout << "sizeof(CA) = " << sizeof(CA) << endl; //为了区分不包含任何成员的类的不同的元素,编译器会自动为类添加一个匿名元素
    cout << "sizeof(CB) = " << sizeof(CB) << endl; //与上面类似,编译器也为CB添加了一个匿名元素
    cout << "sizeof(CC) = " << sizeof(CC) << endl; //虚拟继承中vbptr(virtual base table pointer)占用4个字节

    cout << "sizeof(CD) = " << sizeof(CD) << endl;
    cout << "sizeof(CE) = " << sizeof(CE) << endl; //访问权限控制是在编译期间由编译器控制的,所以虽然不能访问CD类的成员k,这里仍然占用了sizeof(int)大小的空间
    //下面的代码进一步说明上述观点,由于在复杂的类层次结构中,当涉及到虚函数或者虚拟继承等时,有些信息是运行期动态生成的,故请勿效仿以下方法对对象进行修改
    CE e;
    e.printk();
    memset(&e, 0, sizeof(CE));
    e.printk(); //从这里可以看出,上面的memset操作修改了CD类的私有成员k

    cout << "sizeof(CF) = " << sizeof(CF) << endl; //虚函数表指针占有4个字节
}

结构体对齐的具体含义(#pragma pack)

结构体对齐的具体含义(#pragma pack)

作者:panic 2005年4月2日

还是来自csdn的帖子:
主  题:   探讨:内存对齐
作  者:   typedef_chen ((名未定)(我要骗人))
等  级:   
信 誉 值:   100
所属论坛:   C/C++ C++ 语言
问题点数:   50
回复次数:   1
发表时间:   2005-04-02 22:53:27
  
  
朋友帖了如下一段代码:
  #pragma pack(4)
  class TestB
  {
  public:
    int aa;
    char a;
    short b;
    char c;
  };
  int nSize = sizeof(TestB);
  这里nSize结果为12,在预料之中。

  现在去掉第一个成员变量为如下代码:
  #pragma pack(4)
  class TestC
  {
  public:
    char a;
    short b;
    char c;
  };
  int nSize = sizeof(TestC);
  按照正常的填充方式nSize的结果应该是8,为什么结果显示nSize为6呢?

事实上,很多人对#pragma pack的理解是错误的。
#pragma pack规定的对齐长度,实际使用的规则是:
结构,联合,或者类的数据成员,第一个放在偏移为0的地方,以后每个数据成员的对齐,按照#pragma pack指定的数值和这个数据成员自身长度中,比较小的那个进行。
也就是说,当#pragma pack的值等于或超过所有数据成员长度的时候,这个值的大小将不产生任何效果。
而结构整体的对齐,则按照结构体中最大的数据成员 和 #pragma pack指定值 之间,较小的那个进行。

具体解释
#pragma pack(4)
  class TestB
  {
  public:
    int aa; //第一个成员,放在[0,3]偏移的位置,
    char a; //第二个成员,自身长为1,#pragma pack(4),取小值,也就是1,所以这个成员按一字节对齐,放在偏移[4]的位置。
    short b; //第三个成员,自身长2,#pragma pack(4),取2,按2字节对齐,所以放在偏移[6,7]的位置。
    char c; //第四个,自身长为1,放在[8]的位置。
  };
这个类实际占据的内存空间是9字节
类之间的对齐,是按照类内部最大的成员的长度,和#pragma pack规定的值之中较小的一个对齐的。
所以这个例子中,类之间对齐的长度是min(sizeof(int),4),也就是4。
9按照4字节圆整的结果是12,所以sizeof(TestB)是12。


如果
#pragma pack(2)
    class TestB
  {
  public:
    int aa; //第一个成员,放在[0,3]偏移的位置,
    char a; //第二个成员,自身长为1,#pragma pack(4),取小值,也就是1,所以这个成员按一字节对齐,放在偏移[4]的位置。
    short b; //第三个成员,自身长2,#pragma pack(4),取2,按2字节对齐,所以放在偏移[6,7]的位置。
    char c; //第四个,自身长为1,放在[8]的位置。
  };
//可以看出,上面的位置完全没有变化,只是类之间改为按2字节对齐,9按2圆整的结果是10。
//所以 sizeof(TestB)是10。

最后看原贴:
现在去掉第一个成员变量为如下代码:
  #pragma pack(4)
  class TestC
  {
  public:
    char a;//第一个成员,放在[0]偏移的位置,
    short b;//第二个成员,自身长2,#pragma pack(4),取2,按2字节对齐,所以放在偏移[2,3]的位置。
    char c;//第三个,自身长为1,放在[4]的位置。
  };
//整个类的大小是5字节,按照min(sizeof(short),4)字节对齐,也就是2字节对齐,结果是6
//所以sizeof(TestC)是6。

感谢 Michael 提出疑问,在此补充:

当数据定义中出现__declspec( align() )时,指定类型的对齐长度还要用自身长度和这里指定的数值比较,然后取其中较大的。最终类/结构的对齐长度也需要和这个数值比较,然后取其中较大的。

可以这样理解, __declspec( align() ) 和 #pragma pack是一对兄弟,前者规定了对齐的最小值,后者规定了对齐的最大值,两者同时出现时,前者拥有更高的优先级。
__declspec( align() )的一个特点是,它仅仅规定了数据对齐的位置,而没有规定数据实际占用的内存长度,当指定的数据被放置在确定的位置之后,其后的数据填充仍然是按照#pragma pack规定的方式填充的,这时候类/结构的实际大小和内存格局的规则是这样的:
在__declspec( align() )之前,数据按照#pragma pack规定的方式填充,如前所述。当遇到__declspec( align() )的时候,首先寻找距离当前偏移向后最近的对齐点(满足对齐长度为max(数据自身长度,指定值) ),然后把被指定的数据类型从这个点开始填充,其后的数据类型从它的后面开始,仍然按照#pragma pack填充,直到遇到下一个__declspec( align() )。
当所有数据填充完毕,把结构的整体对齐数值和__declspec( align() )规定的值做比较,取其中较大的作为整个结构的对齐长度。
特别的,当__declspec( align() )指定的数值比对应类型长度小的时候,这个指定不起作用。

 

sizeof和内存对齐

简要记录sizeof和内存对齐
    本来,一般是不自己计算sizeof的,知道内存对齐会对sizeof有影响,所以从来不手算,而是代码里写上sizeof。今天又看到http://blog.vckbase.com/smileonce/archive/2005/08/08/10658.html,翻来了http://blog.vckbase.com/billdavid/archive/2004/06/23/509.html ,自己想想还是也记录一下,万一以后自己真的也要计算sizeof,忘了,还能有个提示,也给不是很明白的朋友一个参考。
    struct sample1
   {
        char a;  /// sizeof(char) = 1
        double b; /// sizeof(double) = 8
    };
///default(  缺省#pragam pack(8) ——VC6和VC71,其它编译器,个人未知 )
    ///1+8 = 9 —> 16(  8 < 9 < 16  )

#pragma pack( 4 )
    ///1+8 = 9 —> 12(  8 < 9 < 12  )

#pragma pack( 2 )
    ///1+8 = 9 —> 10(  8 < 9 < 10  )

#pragma pack( 1 )
    ///1+8 = 9 —> 9

#pragma pack( 16 )
    ///1+8 = 9 —> 16(  16—>8 ---- 8 < 9 < 16  )

    struct sample2
    {
        char a;  ///1
        int b;  ///4
    };
#pragma pack( 8 )
    /// 1 + 4  = 5 —> 8(  8 —> 4  )

#pragma pack( 16 )
    /// 1 + 4 = 5 —> 8( 16 —> 4  )

说明:#pragma pack告诉编译器进行内存边界对齐,一般都是采用编译器的设置对整个项目采用同一对齐方案,而且通常为缺省8字节对齐。
 
sizeof(联合)这个值是怎么计算的 内存管理

解惑:sizeof(联合)这个值是怎么计算的

[不要只做技术]在论坛上问如下代码结果为什么是24?

union DATE{    char a;    int i[5];    double b;};DATE max;cout<< sizeof(max) << endl;

这个问题很好回答,并且我把这个问题归结于基本概念题(就是入门书必须介绍的)。我想一般来说,做过内存管理的,对这个语言特性肯定不会陌生。

摘几句The C Programming Language里面讲述这个问题的原话,以说明读书还是必要的:
①联合就是一个结构,②它的所有成员相对于基地址的偏移量都为0,③此结构空间要大到足够容纳最“宽”的成员,④并且,其对齐方式要适合于联合中所有类型的成员。

怕有的兄弟还不明白,特附图一个帮助理解:
char a; => x                                               int i[5]; =>>

 x

 x

 x

 x

 x

x

double b; =>

 x

 

 

 

 

 

 

 

 


该结构要放得下int i[5]必须要至少占4×5=20个字节。如果没有double的话20个字节够用了,此时按4字节对齐。但是加入了double就必须考虑double的对齐方式,double是按照8字节对齐的,所以必须添加4个字节使其满足8×3=24,也就是必须也是8的倍数,这样一来就出来了24这个数字。综上所述,最终联合体的最小的size也要是所包含的所有类型的基本长度的最小公倍数才行。(这里的字节数均指winnt下的值,平台、编译器不同值也有可能不同。)

联合在存储分配的时候用的机会最多,因为很少有像存储分配这样需要给多种不同类型的变量分配空间而又打算尽可能的节约内存的,这很适合联合的特性。上述对齐的方式有个很有趣的用法也就常在存储分配里面使用。(下面依旧用The C Programming Language中的例子作答)

typedef long Align;union header {    struct {        union header *ptr;        unsigned size;    } s;    Align x;}

这里的Align有什么用?作用只有一个,就是强迫分配的结构体按long的长度对齐。