《自定义类型的内存分配问题》

这是我刚学结构体与数据内存分配时经常出错的知识,想要写出来给初学者或是忘记该知识点的人提个醒~

 1、结构体的内存对齐问题

  对于结构体的每一个成员变量,会根据不同类型而产生不同的对齐方式

1.第一个成员在与结构体变量偏移量为0的地址处。

2.其他成员变量要对齐到某个数字(对齐数)整数倍偏移量的地址处。

          其中,对齐数=编译器默认对齐数该成员变量字节大小 的较小值。

3.结构体总大小为最大对齐数的整数倍。(注意:每个成员变量都有一个自己的对齐数)

4.对于结构体嵌套的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体整体大小要是所有对齐数中(包含嵌套结构体对齐数)最大对齐数的整数倍。

下面是个小例题:(以下列题均在vs编译器上编码,其默认对齐数为8)

struct s
{
  double d;
  char c;
  int i;
};
printf("%d\n',sizeof(struct s));

其中double类型的d对齐数为8,而它又是第一个结构体成员变量,所以在与结构体变量偏移量为0的地址处开始储存。char类型的对齐数为1(因为默认的8与char的字节大小1比较,较小值为1,所以其最大对齐数为1。int类型也是如此),所以c存储在起始地址为8(第9个位置上)而int型的i对齐数为4,根据第2点可知,i要存储在12为起始地址的存储空间上。此时整个结构体的内存大小为8+1+3(浪费的结构体空间)+4=16,恰好是最大对齐数8的整数倍,所以该结构体的内存大小为16

这里给大家穿插一个修改默认对齐数的方法:

#pragma pack (4)  //即将编译器的最大对齐数设置为4
   ...  //结构体
#pragma pack ()   //取消设置的最大对齐数 

2、位段在内存中的储存问题

首先先说一说位段是个什么东西,位段的声明和结构体是相似的,有两点不同:

1.位段成员必须是整形(int ,signed int ,unsigned int ,char ...)

2.位段的成员名后有一个冒号和一个数字

struct s
{
  int a : 2 ; //两个比特位
  int b : 5 ; //五个比特位
};

关于位段的内存分配:

1.位段的空间是按照以4个字节(int)或1个字节(char)的方式开辟的。

2.需要知道位段涉及很多不确定因素,是不跨平台的,可移植程序应避免使用位段。

  1)、位段中最大设置比特位数不确定(16位机器最大16,32位机器最大32);

  2)、位段中的成员在内存中从左向右分配,还是从右向左分配没有标准;

  3)、当第二个位段成员较大无法容纳于第一个位段成员剩余位时,是舍弃还是利用剩余位。

优点: 跟结构相比,相同效果的同时更好的节省空间。但存在跨平台问题。

下面是一个例题:(对于vs来说内存是从左向右存储,剩余位舍弃)

struct S
{       //因为是char型所以一个字节一个字节开辟内存空间
  char a : 3 ;
  char b : 4 ;
  char c : 5 ;
  char d : 4 ;
};

int main
{
  struct S a={0};
  a.a = 10;    //二进制表示:1010        存3位:010
  a.b = 20;    //二进制表示:10100       存4位:0100
  a.c = 3;     //二进制表示:011         存5位:00011 
  a.d = 4;     //二进制表示:100         存4位:0100
  return 0;
}

先开辟一个字节(8位):00 00 00 00

存a:                                   00 00 00 10

存b:                                   00 10 00 10   //这里最左边剩下的一位因存不下c而舍弃

开辟第二个字节         :    00 00 00 00

存c:                                   00 00 00 11    //最左边剩下的三位存不下d而舍弃 

开辟第三个字节         :    00 00 00 00     

存d:                                 00 00 01 00  

三个字节的储存内容为:0010 0010 0000 0011 0000 0100

转换成16进制在内存中是:OX 2 2 0 3 0 4

3、枚举类型

enum Sex  //枚举的定义
{              
  MALE,          //默认值为0    (可修改默认值 eg:MALE = 3, )
  FEMALE,        //默认值为1   
};
enum Sex s = MALE;     //枚举的使用
 

可以使用#define定义常量,但枚举更有优点

1.增加代码的可读性和可维护性;

2.与#define定义的标识符相比,枚举存在类型检查,更加严谨;

3.使用枚举可以有效防止命名冲突

4.便于调试(#define定义的符号在调试时内容被替换,导致执行内容与看到的内容不符)

4、联合体(共用体)的内存分配:(同样存在默认对齐数 vs编译器为8 )

  *联合体成员是共用同一块内存空间的,联合体变量大小至少是最大成员变量的大小,同时要是最大对齐数的整数倍(数组的对齐数是元素的对齐数)

union Un
{                  //联合体的声明
  int i;
  char c;
};                 //同一时刻i和c不可同时使用

union Un a;        //对于联合体变量a存在:&a==&(a.i)==&(a.c)

所以联合体变量a的大小为8

通过联合体我们可以来检验当前机器的存储模式:

int check_sys()
{
  union
 {
   char c;       //这里的u是一个匿名结构体类型(创建后只能使用一次)
   int  i;
 }u;
 u.i=1;
 return u.c;     //返回1表示为小端储存,返回0表示为大端储存
}

因为联合体u中的char类型的c与int类型的i是共用一块4字节的内存空间的,所以如果是小端储存模式那么char类型的c刚好读取到存在i中的1,反之大端储存模式则读出0。

好啦,今天就写到这儿吧,希望能帮大家更好的理解结构体在内存中的储存方式。若以上内容有什么错误请大家及时纠正我,我会及时改正的。(谢谢大家ヾ(≧▽≦*)o)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值