使用sizeof()计算结构体大小

下面代码的输出结果是:

#include  
using namespace std; 
#pragma pack(4) 
struct T{    
    char a;    
    short b;    
    char c; 
}; 
#pragma pack()
 
int main() 
{    
    cout << sizeof(T) << endl;    
    return 0; 
}

A. 4 B. 5 C. 8 D. 6

一、sizeof()计算结构体大小

结构体成员按照定义时的顺序依次存储在连续的内存空间,但是结构体的大小并不是简单的把所有成员大小相加,而是遵循一定的规则,需要考虑到系统在存储结构体变量时的地址对齐问题。

  1. 结构体变量中成员的偏移量必须是成员大小的整数倍 (0是任何数的整数倍)
  2. 结构体大小必须是所有成员大小的整数倍 (即所有成员大小的公倍数
  3. 结构体大小计算方式:最后一个成员的偏移量加上其大小

例1:

struct T{    
    char a;    
    short b;    
    char c; 
}; 

【分析】

​ 第一个成员char a的偏移量为0
​ 第二个成员short b的偏移量为前一个成员的偏移量+前一个成员的大小,即0 + 1 = 1,因1不是该成员大小2的整数倍,所以需要调整到2(最小倍数),所以第二个成员的偏移量为2
​ 第三个成员char c的偏移量为第二个成员的偏移量+第二成员的大小2 + 2 = 4, 4是其大小1的整数倍,所以第三个成员的偏移量为4

​ 所以该结构体大小sizeof(T)的计算结果为最后一个成员的偏移量加上其大小即4 + 1 = 5,但5不是所有成员大小(1, 2, 1)的整数倍,所以需要补齐到**6**。

注:偏移量可以理解为结构体成员相对于结构体的起始地址


例2:嵌套的结构体

struct T  
{  
      short a;  
      struct   
      {  
           char b;  
           int c;  
      } tmp;   
      int d;  
};

对于嵌套的结构体,应该将其展开,规则变化为:

  1. tmp结构体的第一个成员的偏移量(代表tmp结构体在整个结构体中的起始地址)应当是tmp结构中最大成员大小的整数倍;
  2. 其它成员的偏移量计算方法不变
  3. 结构体大小sizeof(T)必须是展开后所有成员大小的整数倍

【分析】

​ 第一个成员short a的偏移量为0
​ 第二个成员char b, 其偏移量本是前一个成员的偏移量+前一个成员的大小, 即0 + 2 = 2,但它又是tmp结构体的第一个成员,其偏移量应该是tmp结构体中最大成员int c大小4的整数倍, 所以应该调整到4;
​ 第三个成员int c,偏移量为4 + 1 = 5,调整到其自身大小整数倍8;
​ 第四个成员int d,偏移量为8 + 4 = 12,已经是自身大小整数倍,无需调整;

​ 所以该结构体大小sizeof(T)的计算结果为最后一个成员的偏移量加上其大小即12 + 4 = 1616是展开后所有成员大小(2, 1, 4, 4)的整数倍,所以无需再调整。


例3:包含数组成员的结构体

struct T  
{  
    float f;  
    char p;  
    int arr[3];  
};

将数组看成一个结构体,其中所有成员均为该数组类型变量,同样展开计算。

【分析】

​ 第一个成员float f偏移量为0;
​ 第二个成员char p偏移量为0 + 4 =4,是其大小1的整数倍,无需调整;
​ 第三个成员int arr[3]为数组,展开为三个int类型成员计算
​ …
​ 最后一个成员的偏移量为16

​ 所以该结构体大小sizeof(T)的计算结果为最后一个成员的偏移量加上其大小即16 + 4 = 2016是展开后所有成员大小(4, 1, 4)的整数倍,所以无需再调整。

二、#pragma pack(n)

编译器中提供了#pragma pack(n)来设定变量以n字节对齐方式。n字节对齐就是说变量存放的起始地址的偏移量有两种情况:

  • 如果n大于等于该变量所占用的字节数,那么偏移量必须满足默认的对齐方式
  • 如果n小于该变量的类型所占用的字节数,那么偏移量为n的倍数,不用满足默认的对齐方式。

结构的总大小也有个约束条件,分下面两种情况:

  • 如果n大于所有成员变量类型所占用的字节数,那么结构的总大小必须为占用空间最大的变量占用的空间数的倍数;
  • 否则必须为n的倍数。

总结一下,就是两条判断原则:

  1. 每个成员的偏移量必须是**x**的倍数, x = min(n,该成员大小)

  2. 结构体的总大小必须是**y**的倍数, y = min(n,占用空间大的变量的字节数)

例:

#pragma pack(4) 
struct T{    
    char a;    
    short b;    
    char c; 
}; 
#pragma pack()

【分析】

n = 4;
​ 第一个成员char a,其偏移量为0(无论n的值和第一个成员的类型,第一个成员的偏移量总是0);
​ 第二个成员short b, 计算x = min(4, 2) = 2,其偏移量为第一个成员的偏移量+第一个成员的大小,即0 + 1 = 1,因为1不是x的倍数,所以偏移量调整为2
​ 第三个成员char c,计算x = min(4, 1) = 1的偏移量为第二个成员的偏移量+第二成员的大小,即2 + 2 = 4, 4x的整数倍,所以第三个成员的偏移量为4

​ 计算y = min(4, max(1, 2, 1)) = 2, 该结构体大小sizeof(T)的计算结果为最后一个成员的偏移量加上其大小即4 + 1 = 5,但5不是y的整数倍,所以需要补齐到6

三、总结

  1. 计算每个成员的偏移量:第一个成员偏移量总是0;其它成员偏移量=前一个成员偏移量+前一个成员大小, 然后根据规则调整;
  2. 计算结构体大小 = 最后一个成员偏移量 + 最后一个成员大小;再根据规则调整。
  3. 标注#pragma pack(n)和不标注的规则不一样, 嵌套结构体(或者带数组成员的结构体)的规则也发生变化。

你可能感兴趣的:(C++试题,结构体,sizeof)