结构体的大小

来源:互联网 发布:navicat for linux 64 编辑:程序博客网 时间:2024/05/21 08:45
结构体字节对齐   ​

在用sizeof运算符求算某结构体所占空间时,并不是简单地将结构体中所有元素各自占的空间相加,这里涉及到内存字节对齐的问题。从理论上讲,对于任何变量的访问都可以从任何地址开始访问,但是事实上不是如此,实际上访问特定类型的变量只能在特定的地址访问,这就需要各个变量在空间上按一定的规则排列,而不是简单地顺序排列,这就是内存对齐。

​内存对齐的原因:

  1)某些平台只能在特定的地址处访问特定类型的数据;

   2)提高存取数据的速度。比如有的平台每次都是从偶地址处读取数据,对于一个int型的变量,若从偶地址单元处存放,则只需一个读取周期即可读取该变量;但是若从奇地址单元处存放,则需要2个读取周期读取该变量。win32平台下的微软C编译器对齐策略:

    1)结构体变量的首地址能够被其最宽数据类型成员的大小整除。编译器在为结构体变量开辟空间时,首先找到结构体中最宽的数据类型,然后寻找内存地址能被该数据类型大小整除的位置,这个位置作为结构体变量的首地址。而将最宽数据类型的大小作为对齐标准。

    2)结构体每个成员相对结构体首地址的偏移量(offset)都是每个成员本身大小的整数倍,如有需要会在成员之间填充字节。编译器在为结构体成员开辟空间时,首先检查预开辟空间的地址相对于结构体首地址的偏移量是否为该成员大小的整数倍,若是,则存放该成员;若不是,则填充若干字节,以达到整数倍的要求。

     3)结构体变量所占空间的大小必定是最宽数据类型大小的整数倍。如有需要会在最后一个成员末尾填充若干字节使得所占空间大小是最宽数据类型大小的整数倍。

下面看一下sizeof在计算结构体大小的时候具体是怎样计算的

1.test1  

空结构体

typedef struct node1

{

}S;

则sizeof(S)=1;或sizeof(S)=0;在C++中占1字节,而在C中占0字节。

2.test2

typedef struct node1

{

    int a;

    char b;

    short c;

}S1;

则sizeof(S1)=8。这是因为结构体node1中最长的数据类型是int,占4个字节,因此以

4字节对齐,则该结构体在内存中存放方式为

|--------int--------|   4字节

|char|----|--short-|   4字节

总共占8字节                                                             ​

3.test3

typedef struct node2

{

    char a;

    int b;

    short c;

}S2;

则siezof(S3)=12.最长数据类型为int,占4个字节。因此以4字节对齐,其在内存空间存放方式如下:

|char|----|----|----|  4字节

|--------int--------|  4字节

|--short--|----|----|  4字节

总共占12个字节

4.test4  含有静态数据成员  

typedef struct node3

{

    int a;

    short b;

    static int c;

}S3;

则sizeof(S3)=8.这里结构体中包含静态数据成员,而静态数据成员的存放位置与结构体实例的存储地址无关(注意只有在C++中结构体中才能含有静态数据成员,而C中结构体中是不允许含有静态数据成员的)。其在内存中存储方式如下:

|--------int--------|   4字节

|--short-|----|----|    4字节

而变量c是单独存放在静态数据区的,因此用siezof计算其大小时没有将c所占的空间计算进来。

5.test5  结构体中含有结构体

typedef struct node4

{

    bool a;

    S1 s1;

    short b;

}S4;​

则sizeof(S4)=16。是因为s1占8字节,而s1中最长数据类型为int,占4个字节,bool类型1个字节,short占2字节,因此以4字节对齐(以单个最大类型为基准对齐),则存储方式为

|-------bool--------|  4字节

|-------s1----------|  8字节

|-------short-------|  4字节

6.test6

typedef struct node5

{

    bool a;

    S1 s1;

    double b;

    int c;

}S5;

则sizeof(S5)=32。是因为s1占8字节,而s1中最长数据类型为int,占4字节,而double占8字节,因此以8字节对齐,则存放方式为:

|--------bool--------|    8字节

|---------s1---------|    8字节

|--------double------|    8字节

|----int----|---------|     8字节 

7.test7 

若在程序中使用了

#pragmapack(n)

命令强制以n字节对齐时,默认情况下n为8.则比较n和结构体中最长数据类型所占的字节大小,取两者中小的一个作为对齐标准。若需取消强制对齐方式,则可用命令#pragmapack()

​如果在程序开头使用命令#pragmapack(4),对于下面的结构体

typedef struct node5

{

    bool a;

    S1 s1;

    double b;

    int c;

}S5;

则sizeof(S5)=24.因为强制以4字节对齐,而S5中最长数据类型为double,占8字节,因此以4字节对齐。在内存中存放方式为:

  |-----------a--------|   4字节

  |--------s1----------|   4字节

  |--------s1----------|   4字节

  |--------b-----------|   4字节

  |--------b-----------|   4字节

  |---------c----------|    4字节

总结一下,在计算sizeof时主要注意一下几点:

1)若为空结构体,则只占1个字节的单元

​2)若结构体中所有数据类型都相同,则其所占空间为 成员数据类型长度×成员个数

若结构体中数据类型不同,则取最长数据类型成员所占的空间为对齐标准,数据成员包含另一个结构体变量t的话,则取t中最 长数据类型与其他数据成员比较,取最长的作为对齐标准,但是t存放时看做一个单位存放,只需看其他成员即可。

3)若使用了#pragma pack(n)命令强制对齐标准,则取n和结构体中最长数据类型占的字节数两者之中的小者作为对齐标准。

另外除了结构体中存在对齐之外,普通的变量存储也存在字节对齐的情况,即自身对齐。编译器规定:普通变量的存储首地址必须能被该变量的数据类型宽度整除。

补充:​

由于计算机硬件原因,每个数只能放在其字节数整数倍的地址上(用空间换时间)​

eg1:

struct A                                                     struct A1

{                                                                 {

char a;  //1+3为4的倍数                               int a;  //4

int b;  //4                                                     char b;  //1​

};  //8字节                                                   };  //8字节<防止定义结构体数组>,

                                                               单个变量还是只能存储在其地址的整数倍上

 |char|----|----|----|  4字节                        |--------int--------|  4字节

|--------int--------|  4字节                         |char|----|----|----|  4字节

共占8个字节                                              共占8个字节

eg2:

struct D                                           struct E

{                                                         {

char a;  //1+3                                    char a;  //1+1

int b;  //4                                           short b;  //2

short c;  //2+6                                   int c;   //4

double d;  //8                                    double d;  //8​

};  //24字节                                         };  //16字节

由上eg2可知,在结构体中定义数据的顺序会影响结构体的大小(由小到大)




*************************************************************************

sizeof(类):
若类中含虚函数,就存在指向虚函数表的指针,4个字节;
静态数据成员,存储在全局数据区,而 sizeof()计算的是栈中变量所占字节;

1.类中一旦有virtual修饰的成员函数,编译器会构建虚函数表,在该类的对象中会存放一个指向虚函数表的指针,在 32位机中该指针和其他指针一样占用4字节。 +4 
2.static作为静态变量是对类而言的,为所有对象共有,不计算对象的占用空间。 +0 
3.神马都没有或仅有非虚成员函数的类或仅有类型声明的类(如typdef),均为空类。对于空类,编译器会构造占用1字节空间的变量(通常为char型); +1 ;若该类为基类,则继承自空基类的子类不计算这1字节的空间,仅计算子类所占空间(编译器原理:空基类优化)。 +0