(改写)结构在内存中的存储分配--对齐原则

知识点1:结构在内存中的存储分配--对齐原则


首先,我们应该知道:编译器对变量进行存储分配时,满足以下优化原则:

    n字节元素首地址需要能够被n整除。该原则称为对齐原则。

         现阐述结构体成员的内存分配规律:


         从结构体的首地址开始,向后依次为每个成员寻找一个满足条件的首地址X,该条件为:

X%N=0N为各个成员的对齐参数)。结构体的首地址选取也满足该条件,此时N为结构体的对齐参数。关于结构体中成员对齐参

数:N=min(sizeof(成员类型)n)其中n为编译器中可设置的结构体成员对齐参数1,2,4,8,16字节等)。整个结构体的对齐参数

该结构体中成员的最大的对齐参数。另外,结构体的长度应为成员变量中最大对齐参数的最小整数倍(要至少足以容纳所有的变

量)。


举例说明:

#include<iostream.h>
#pragma pack(8) //设置编译器中结构体成员对齐参数为8
struct example1
{
short a;
long b;
};
struct example2
{
char c;
example1 struct1;
short e;    
};
#pragma pack()
int main(int argc, char* argv[])
{
example2 struct2;
cout << sizeof(example1) << endl;
cout <<sizeof(example2) << endl;
cout << (unsigned int)(&struct2.struct1) - (unsigned int)(&struct2) <<endl;
return 0;

请写出打印结果。已知sizeof(short)=2,sizeof(long)=4,sizeof(char)=1.


结果为:

8

16

4

解析example1中,a的对齐参数为2b的对齐参数为4example1类型的长度为8,对齐参数为4

example2中,c的对齐参数为1struct1的对齐参数为4e的对齐参数为2example2类型的长度为16example2类型的对齐参数为4.


我们若在主函数中增加如下语句:

example1 struct1;

cout<<&struct1.a<<” ”<<&struct1 .b<<endl;
cout<<&struct2.c<<” ”<<&struct2 .struct1<<” ”<<&struct2.e<<endl;

我们将会得到如下结果:

2293476 2293480

2293484 2293488 2293496


说明

2293476是一个能被4整除的地址(依赖struct1的对齐参数4),2293480是一个存储完struct1.a之后紧接着能被4整除的地址(依赖struct1.b的对齐参数4

所以struct1的存储方式为2293476开始存储2个字节,然后填充2个字节,2293480开始存储4个字节,不继续填充(因为满足了最大对齐参数的最小整数倍,且可以完整存

储成员变量)

2293484是一个能被4整除的地址(依赖struct2的对齐参数4),2293488是一个存储完struct2.c之后紧接着能被4整除的地址(依赖struct2.struct1的对齐参数4),2293496

存储完struct2.struct1之后紧接着能被2整除的地址(依赖struct2.e的对齐参数2

所以struct2的存储方式为2293484开始存储1个字节,然后填充3个字节,2293488开始存储8个字节(struct2.struct1的长度为8),接着2293496开始存储2个字节,此时

struct2所占的内存长度为14,该结构体成员的最大对齐参数为4,大于等于14且是4的最小整数倍的数应为16,即struct2的长度应为16,所以,接着填充2个字节。



按照以上阐述原则,若将源程序中#pragma pack(8)改为#pragma pack(2)则原程序结果应为:

6

10

2


注意:

1.当结构体成员为数组时,在分配内存时,并不会将整个数组当成一个成员来对待,而是将数组中的每个元素当成一个成员来分配内存,分配规则不变。

eg:

struct A
{
char c; //1byte
double d; //8byte
short s; //2byte
char szBuf[5];
};


当编译器对齐参数设为8byte时,则该结构体的长度为24个字节。


2.当结构体中有成员不是一个完整的类型单元,而是位段时

(位段C语言允许在一个结构体中以位为单位指定其成员所占内存长度,这种以位为单位的成员成为“位段”或“位域”。利用位段能够用较少的位数存储数据。)


对于位段成员,首先按其类型分配空间,如int型就分配4个连续的存储单元。接下来,若是相同类型的位段成员就连续存放,公用这4个存储单元,当该类型长度不够用时,就

另起一个该类型长度的存储空间。其它分配原则不变。

eg:Intel笔试)

#include“stdafx.h”
#include <iostream.h>
struct bit
{
int a:3;
int b:2;
int c:3;
}
int main(int argc,char *argv[])
{
bit s;
char *c=(char *)&s;
*c=0x99;
cout<<s.a<<endl<<s.b<<endl<<s.c<<endl;
return 0;
}

Output?

结果是:

1

-1

-4


分析:0x99在内存中表示为10011001,所以,s.a=001;s.b=11; s.c=100.

int为有符号数时,a=1;b=-1,(负数在内存中以反码存储,b的原码为11,表示-1)c=-4,(c的源码为100,表示-4)


int为无符号数时,a=1;b=3; c=4.


注意:

1.假设存储单元中,位段的空间方向由低位(右侧)到高位(左侧)分配

如:下面的结构体(假定int2个字节

struct str
{
unsigned a:2;
unsigned b:3;
unsigned c:4;
int d;
};
str str1;

str1在内存中的空间分配示意图如下:


str1在内存中实际表示则为:


分配过程:先定义的a2个位),被分配到高16位的低位上,紧接着是b3个位),后面的成员分配依此类推。最后的int型,因为是16位,高16位剩余的7个位不能容纳它,

i被分配到低16位。

2.如果一个位段要从下一个字节开始存放,可采用如下定义:

struct str
{
unsigned a:1;
unsigned b:2;
unsigned :0;
unsigned c:3;
}

它在内存中对应结构如下:

另外,关于位段要知道:

一、可以定义无名位段如unsigned :3;但该长度的空间是不可用的。

二、不能够定义位段数组。

三、给位段赋值时,遵循自动阶段原则。即当某个位段长度为2位时,却被赋值为5,则要进行截断。

如:struct bit中有unsigned a:2;bit.a=5;则此时a的值应该为等于1.

 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值