栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

内存空间的对齐分布

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

内存空间的对齐分布

1.结构体对齐规则:

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

(2)其他成员变量要对齐到对齐数的整数倍的地址处。

对齐数=编译器默认对齐数与该成员大小的较小值。

(3)结构体总大小为最大对齐数的整数倍

(4)如果嵌套了结构体的情况,嵌套的结构体对齐到自己最大对齐数的整数倍处,

结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

(5)visual studio 中的默认对齐数=8,可通过#pragma pack(x)修改默认对齐数的大小,

也可通过#pragma pack()取消对默认对齐数的修改,若想取消默认对齐数可写成#pragme pack(1)。

(6)Eg:

#pragma pack(4)
int main(int argc, char* argv[])
{
  struct tagTest1
  {
    short a;
    char d; 
    long b;   
    long c;   
  };
  struct tagTest2
  {
    long b;   
    short c;
    char d;
    long a;   
  };
  struct tagTest3
  {
    short c;
    long b;
    char d;   
    long a;   
  };
  struct tagTest1 stT1;
  struct tagTest2 stT2;
  struct tagTest3 stT3;

  printf("%d %d %d", sizeof(stT1), sizeof(stT2), sizeof(stT3));//12 12 16
  return 0;
}
#pragma pack()

2.为什么会存在内存对齐:

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

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

(3)总体来说:结构体内存对齐是拿空间来换取时间。

(4)在设计结构体时,尽量让占空间小的集中在一起,这样既可以满足对齐,又可以节省空间

3.位段

(1)位段的成员必须是int ,unsigned int ,或singed int。

(2)位段的成员名后面有一个冒号和一个数字,例:

int lin : 1 ;

表示:lin这个成员只占1个bit位

(3)位段的跨平台问题:int位段被当成有符号还是无符号是不确定的,

位段中最大位数目在16位机器中最大是16,32位机器最大是32,

位段中的成员在内存中从左向右分配,还是从右向左分配尚未定义,、

当一个结构体包含两个位段,第二个位段成员比较大时,无法容纳于第一个位段剩余的位时,

是舍弃剩余位置还是利用,还不确定

(4)总结:跟结构体相比较,位段可以达到相同的效果,可以很好的节省空间,却存在跨平台的问题。

(5)Eg:

int main()
{
  unsigned char puc[4];
  struct tagPIM
  {
    unsigned char ucPim1;
    unsigned char ucData0 : 1;
    unsigned char ucData1 : 2;
    unsigned char ucData2 : 3;
  }*pstPimData;
  pstPimData = (struct tagPIM*)puc;
  memset(puc,0,4);
  pstPimData->ucPim1 = 2; 
  pstPimData->ucData0 = 3;
  pstPimData->ucData1 = 4;
  pstPimData->ucData2 = 5;
  printf("%02x %02x %02x %02xn",puc[0], puc[1], puc[2], puc[3]);//02 29 00 00
  return 0;
}

注释:puc是一个char数组,每次跳转一个字节,结构体不是,它只有第一个元素单独享用一字节,其他三个元素一起共用一字节,所以puc被结构体填充后,本身只有两个字节会被写入,后两个字节肯定是0,然后第一个字节是2就是2了,第二个字节比较麻烦,首先ucData0给了3其实是越界了,1位的数字只能是0或1,所以11截断后只有1,同理ucData1给的4也是越界的,100截断后是00,只有5的101是正常的。填充序列是类似小端的低地址在低位,所以排列顺序是00 101 00 1。也就是0010 1001,即十六进制表示0x29

4.枚举:

(1)什么是枚举:一个集的枚举是列出某些有穷序列集的所有成员的过程,或者是一种特定类型对象的计数,这两种类型经常(但不总是)重叠,是一个被命名的整型常数的集合。

形式:

enum 枚举名
{
    成员1[=整型常数];
    成员2[=整型常数];
    ...
    成员n[=整型常数];
}枚举变量;

(2)如果枚举没有初始化,从第一个元素开始依次赋值0,1,2,......。当枚举中某个元素成员赋值以后,其后的成员按依次加1的规则确定其值。

(3)枚举类型所占空间的大小:当枚举成员小于4个字节时,所占空间为4个字节,

当枚举成员的值大于4个字节时,所占空间为8个字节(最大范围不能超过long long类型)

(4)枚举的优点:增加代码的可读性和维护性,和#define定义的标识符比较枚举有类型检查,

更加严谨,防止了命名污染(封装),便于调试,使用方便,一次可定义多个变量。

(5)Eg:

enum ENUM_A
{
		X1,
		Y1,
		Z1 = 255,
		A1,
		B1,
}lin;
enum ENUM_A enumA = Y1;
enum ENUM_A enumB = B1;
printf("%dn",sizeof(lin));//4
printf("%d %dn", enumA, enumB);//1 257

注释:枚举成员小于4个字节,所占空间的大小为4个字节,枚举默认从0开始,所以X1是0,故Y1是1,给了数字后会根据数字向后推,那么Z1是255,A1是256,所以B1是257。

5.联合体(共用体)

(1)联合体的定义:联合体是一种特殊的自定义类型,这种类型定义的变量也包含一系列的成员,特征是这些成员共用同一块空间。

形式:

union lin
{
    类型名 成员名1;
    类型名 成员名2;
    ......
    类型名 成员名n;
};

(2)联合的特点:联合的成员是共用同一块空间,联合的大小,至少是最大成员的大小。

(3)Eg:运用联合体判断大小端

#include
int lin()
{
	union ln
	{
		char c;
		int i;
	}u;
	u.i = 1;
	return u.c;
}
int main()
{
	if (lin() == 1)
	{
		printf("小端");
	}
	else
	{
		printf("大端");
	}
	return 0;
}

(4)联合体也可以存在内存对齐

#include 
union Un
{
	short s[7];
	int n;
};
int main()
{
  printf("%dn", sizeof(union Un));//16
  return 0;
}

注释:结构体向int对齐,7个short一共是14字节,对齐后是16字节。n是单独的4字节,由于是union,所以n与s共用空间,只取最长的元素,故占用16字节。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/779283.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号