sizeof(union) 、sizeof(struct) 和 内存对齐技术

在32位系统环境,编译选项为4字节对齐,那么sizeof(A)和sizeof(B)是:
    struct A
    {
     int a;
     short b;
     int c;
     char d;
    };
    struct B
    {
     int a;
     short b;
     char c;
     int d;
    }; 

A.16,16
B. 13,12
C. 16,12
D. 11,16

选择C:这类题目考察的是内存对齐:结构体(struct)(或联合体(union))的数据成员,
每个数据成员的对齐按照编译选项指定的数据成员自身长度中,最长的那个。
A:4(int)+4(short不够4,补2)+4(int)+4(char 不够4,补3) = 16
B:4(int)+4(char 为1,short为2 ,补1)+4(int) = 12

long long 的级别高于 long ,long 的级别高于 int ,
int 的级别高于 short ,short 的级别高于 char 。

32位系统:
char   ,unsigned char   :1个字节
short ,unsigned short     :     2 字节
int 、unsigned int,  float、long ,  unsigned long,   *    : 4 字节,
double  , long double   ,  long long   :  8 字节

64位系统
char,  unsigned char  :   1个字节
short , unsigned short   :    2 字节
int 、unsigned int,  float   4字节
long ,double  , long double ,long long , *     8 字节 
(long 有可能是4字节,由编译器决定)

// 对齐原则:每一成员需对齐为后一成员类型的倍数
// 补齐原则:最终大小补齐为成员类型最大值的倍数

structA
{
 inta;     // 4
 shortb;   // (4) + 2 = 6 下一元素为 int,需对齐为 4 的倍数, 6 + (2) = 8
 intc;     // (8) + 4 = (12)
 chard;    // (12) + 1 = 13, 需补齐为 4 的倍数,13 + (3) = 16
};

structB
{
 inta;     // 4
 shortb;   // (4) + 2 = 6,下一成员为 char 类型,不考虑对齐
 charc;    // (6) + 1 = 7,下一成员为 int 类型,需对其为 4 的倍数,7 + (1) = 8
 intd;     // (8) + 4 = 12,已是 4 的倍数
}

一,union:C/C++关键字 共用体(联合)

共用体的声明和共用体变量定义与结构十分相似。形式为:

union 共用体名
{   
	数据类型 成员名;   
	数据类型 成员名;   
	...   
} 变量名; 

共用体表示几个变量共用一个内存位置,在不同的时间保存不同的数据类型和不同长度的变量.在union中,所有的共用体成员共用一个空间,并且同一时间只能储存其中一个成员变量的值。当一个共用体被声明时, 编译程序自动地产生一个变量, 其长度为联合中最大的变量长度的整数倍(特别注意数组)

例子一:

union foo{
  int i;
  char c;
  double k;
  };

sizeof(foo); //double最长占用8字节,所以union foo大小为8字节

例子二:

union A  
{
    int a[5];       //20
    short b;     //2
    double c;    //8
    char p2;     //1
};
 
struct B { 
	int n; // 4字节 
	A a; // 24字节 
	char c[10]; // 10字节 
};

sizeof(A) ; //24 而不是20 ???
sizeof(B) ; //48 而不是???
对齐: 分配内存时,每个成员放在长度倍数位置,如果不够,补位对齐
补齐: 对整个结构变量的空间要求总长度一定是最长的成员的倍数,不够补齐不管是对齐还是补齐,最长的成员长度超过4时,以4计。

A实际占用内存大小为 20字节,但是要跟 8个字节的变量double的整数倍,对齐所以为 24;

由于A实际占用24字节,则可以想象B实际占用38字节,但A是8字节对齐的,所以int n和char c[10]也需要8字节对齐,总共8+24+16=48 字节。

例子三:

union f   
{   
	char s[10];   
	int i;   
};

sizeof(f); // 12
解释:在这个union中,foo的内存空间的长度为12,是int型的3倍,而并不是数组的长度10。
若把int改为double,则foo的内存空间为16,是double型的两倍。

二,struct

具体说明见内存对齐例子

三,内存对齐

1)概念:“内存对齐”应该是编译器的“管辖范围”。编译器为程序中的每个“数据单元”安排在适当的位置上。

2)原因:

1、平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。

2、性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因是:为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。

3)对齐规则

每个特定平台上的编译器都有自己的默认“对齐系数”(也叫对齐模数)。程序员可以通过预编译命令#pragma pack(n),n=1,2,4,8,16来改变这一系数,其中的n就是你要指定的“对齐系数”。

规则:

1、数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员的对齐按照#pragma pack指定的数值和这个数据成员自身长度中,比较小的那个进行。

2、结构(或联合)的整体对齐规则:在数据成员完成各自对齐之后,结构(或联合)本身也要进行对齐,对齐将按照#pragma pack指定的数值和结构(或联合)最大数据成员长度中,比较小的那个进行。

3、结合1、2可推断:当#pragma pack的n值等于或超过所有数据成员长度的时候,这个n值的大小将不产生任何效果。

pragma pack(1) 时候

1>数据成员对齐:

#pragma pack(1)
struct test_t 
{
	int a;      /* int型,  长度4 > 1 按1对齐;起始offset=0 0%1=0;存放位置区间[0,3] */
	char b;     /* char型, 长度1 = 1 按1对齐;起始offset=4 4%1=0;存放位置区间[4] */
	short c;    /* short型,长度2 > 1 按1对齐;起始offset=5 5%1=0;存放位置区间[5,6] */
	char d[6];  /* char型, 长度1 = 1 按1对齐;起始offset=7 7%1=0;存放位置区间[7,C] */
};/*char d[6]要看成6个char型变量*/

sizeof(test_t) ; //输出为13

2>整体对齐

整体对齐系数 = min((max(int,short,char), 1) = 1

整体大小(size)=$(成员总大小) 按 $(整体对齐系数) 圆整 = 13 /13%1=0/

pragma pack(2) 时候

1>成员数据对齐

  #pragma pack(2) 
 
  struct test_t { 
      int a; /* int型,长度4 > 2 按2对齐;起始offset=0 0%2=0;存放位置区间[0,3] */ 
      char b; /* char型,长度1 < 2 按1对齐;起始offset=4 4%1=0;存放位置区间[4] */ 
     short c; /* short型,长度2 = 2 按2对齐;起始offset=6 6%2=0;存放位置区间[6,7] */ 
      char d[6]; /* char型,长度1 < 2 按1对齐;起始offset=8 8%1=0;存放位置区间[8,D] */ 
  }; 

成员总大小=14
  
  2> 整体对齐

整体对齐系数 = min((max(int,short,char), 2) = 2

整体大小(size)=$(成员总大小) 按 $(整体对齐系数) 圆整 = 14 /* 14%2=0 */

四,终极例子

入门例子一:

union A
{
	int t;   //4
	short m; //2
	char p;  //1
};
	
struct B  
{
    A a;          //4
    double c;    //8
    char p2;     //1
};

sizeof(B); // 24

B中采用double长度 8字节对齐方式,所以A在结构体B内 变成8字节。实际B占用8+8+1=17。再填充成8的整数倍后位 24字节

变态例子二:

struct B  
{
    union A
	{
		int t;   //4
		short m; //2
		char p;  //1
	} ;
    double c;    //8
	char p2;     //1
};

sizeof(B); //16 : 4+8+1=13 最大类型 8的最小倍数为 16

struct B
{
	union A
	{
		int t;   //4
		short m; //2
		char p;  //1
	}A;
	double c;    //8
	char p2;     //1
};

sizeof(B); //24

这里很神奇,在union A后定义一个{ …}A; 则sizeof(B)=24;

struct B
{
	typedef union A
	{
		int t;   //4
		short m; //2
		char p;  //1
	}AA;
	double c;    //8
	char p2;     //1
};

sizeof(B) = 16;

struct B
{
	union A
	{
		int t;   //4
		short m; //2
		char p;  //1
	}AA;
	double c;    //8
	char p2;     //1
};

sizeof(B) = 24;

为什么??????union这里的改变是怎么影响sizeof(B)的呢???????


附件:VS2017中示例代码

#include <stdio.h>

typedef struct  BB
{
	union AA  //没有typedef
	{
		int t;   //4  
		short m; //2  
		char p;  //1  
	} AA;
	double c;    //8  
	char p2;     //1  
}BB;

typedef union  A //有typedef
{
	int t;   //4  
	short m; //2  
	char p;  //1  
}A;

typedef   struct   B
{
	A a;          //4  
	double c;    //8  
	char p2;     //1  
}B;


int main()
{
	printf("BB:%d\n", sizeof(BB));
	printf("A:%d\n", sizeof(A));
	printf("B:%d\n", sizeof(B));

	return 0;
}

在这里插入图片描述

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
struct s1 { char a; double b; int c; char d; }; struct s2 { char a; char b; int c; double d; }; cout<<sizeof(s1)<<endl; // 24 cout<<sizeof(s2)<<endl; // 16 同样是两个char类型,一个int类型,一个double类型,但是因为对界问题,导致他们的大小不同。计算结构体大小可以采用元素摆放法,我举例子说明一下:首先,CPU判断结构体的对界,根据上一节的结论,s1和s2的对界都取最大的元素类型,也就是double类型的对界8。然后开始摆放每个元素。 对于s1,首先把a放到8的对界,假定是0,此时下一个空闲的地址是1,但是下一个元素d是double类型,要放到8的对界上,离1最接近的地址是8 了,所以d被放在了8,此时下一个空闲地址变成了16,下一个元素c的对界是4,16可以满足,所以c放在了16,此时下一 个空闲地址变成了20,下一个元素d需要对界1,也正好落在对界上,所以d放在了20,结构体在地址21处结束。由于s1的大小需要是8的倍数,所以21-23的空间被保留,s1的大小变成了24。 对于s2,首先把a放到8的对界,假定是0,此时下一个空闲地址是1,下一个元素的对界也是1,所以b摆放在1,下一个空闲地址变成了2;下一个元素c的对界是4,所以取离2最近的地址4摆放c,下一个空闲地址变成了8,下一个元素d的对界是8,所以d摆放在8,所有元素摆放完毕,结构体在15处结束,占用总空间为16,正好是8的倍数。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值