您当前的位置:首页 > 计算机 > 编程开发 > C语言

C语言字节对齐

时间:12-20来源:作者:点击数:
什么是字节对齐?

现代计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定类型变量的时候经常在特 定的内存地址访问,这就需要各种类型数据按照一定的规则在空间上排列,而不是顺序的一个接一个的排放,这就是对齐。

为什么要字节对齐?

字节是否对齐关系到CPU访问数据时的效率问题,假设一个CPU每次总是从内存中取出4个字节,从内存编号为0的地方开始,现在我定义一个char a,定义一个int b,让他们按顺排列在内存中,就是这样的:

char a占用1个字节,int a占用4个字节,CPU每次总是取4个字节,这时我想要取b时,需要先取出0-3,再取出4-7,然后将1-4拼在一起,这样就需要取两次,但是,如果我让char aint b按照特定的顺序排列:

这样我只需要取一次就能将b取出,提升了CPU的工作效率。

字节对齐的概念和规则

概念:

  • 数据类型自身的对齐值:对于char型数据,其自身对齐值为1,对于short型为2,对于int,float类型,其自身对齐值为4,对于double型,其自身对齐值为8(单位字节)。
  • 结构体或者类的自身对齐值:其成员中自身对齐值最大的那个值。
  • 指定对齐值:#pragma pack (value)时的指定对齐值value。
  • 数据成员、结构体和类的有效对齐值:自身对齐值和指定对齐值中小的那个值。

规则:

有效对齐值N是最终用来决定数据存放地址方式的值,最重要。

有效对齐N,就是表示“对齐在N上”,也就是说该数据的"存放起始地址%N=0"。而数据结构中的数据变量都是按定义的先后顺序来排放的。第一个数据变量的起始地址就是数据结构的起始地址。结构体的成员变量要对齐排放,结构体本身也要根据自身的有效对齐值圆整。

举例说明

代码如下

#include <stdio.h>

struct Test
{
	char a;
	int b;
	short c;
};
int main(void)
{
	struct Test t = {'a',1,2};
	printf("%d \n",sizeof(t));
	getchar();
	return 0;
}

如果我们不知道字节对齐规则,那么一定会认为这个结构体的大小是这样的,char类型1个字节,int类型4个字节,short类型2个字节一共7个字节,执行一下看看结果:

执行之后的结果是12,我们先来看一下反汇编代码:

根据反汇编代码我们可以看到a,b,c中的值分别存放在ebp-0ch,ebp-8,ebp-4这三个地方,我们到内存中看一下他们是怎么排列的:

可以看到char占用1个字节,int占用4个字节,short占用2个字节,但是并没有我们想象的那样紧挨着排放,而是有一定的排放规则。这里就体现出了字节对齐,因为我这里是32位的机器,默认是4字节对齐,下面来详细的说一下是怎么排列的:

假设基址为0012FF3C,从偏移地址为0的位置开始存放

  • 根据上面的字节对齐规则,a是char类型,自身对齐值就是1字节,b是int类型,自身对齐值是4字节,c是short类型,自身对齐值是2字节
  • 结构体的自身对齐值:结构体中自身对齐值最大的那个,根据上面的分析这个结构体的自身对齐值为4字节
  • 指定对齐值:因为我们没有指定对齐值,使用的是默认的4字节对齐
  • 数据成员有效对齐值:自身对齐值和指定对齐值中小的那个值,a的自身对齐值是1,指定对齐值是4,较小值是1,那么a的对齐值就是1;b的自身对齐值是4,指定对齐值是4,较小值是4,那么a的对齐值就是4;c的自身对齐值是12,指定对齐值是4,较小值是2,那么c的对齐值就是2,假设有一个变量是long类型,那么这个变量的对齐值应该为4.

这样,a占用1个字节,存放在偏移地址为0的内存,0%1=0,没有问题。

b占用4个字节,如果将他挨着变量a存放,也就是存放在偏移地址为1的位置,1%4=1,这样就存在问题了,所以b存放到偏移地址为4的位置,4%4=0。

最后,c占用2个字节,如果将他挨着b存放,也就是存放到偏移地址为8的位置,8%2=0,没有问题,所以c存放到 偏移地址为8的位置,占两个字节。

这样就排列好了,这时,发现a,b,c一共才占用了10个字节,因为结构体还没有根据自身有效对齐值圆整,根据上面的分析,这个结构体的自身对齐值是4,12%4=0,所以结构体会再占用两个字节,10和11,也就是A和B,这样就得出这个结构体的实际大小是12。

当然,除了使用默认的对齐值,我们还可以自己使用#pragma pack (value)指定对齐值:

#include <stdio.h>
#pragma pack (2)

struct Test
{
	char a;
	int b;
	short c;
};
int main(void)
{
	struct Test t = {'a',1,2};
	printf("%d \n",sizeof(t));
	getchar();
	return 0;
}

这将对齐值设置成2,那么a,b,c在内存中的排列方式应该是这样的:

要注意,这里的int b的自身对齐值是4字节,有效对齐值是2字节。

方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门