c语言字节对齐作用,C语言的字节对齐(Byte Alignment)

什么是字节对齐?

字节对齐(也叫内存对齐)是为了提高内存存取效率,便于CPU快速访问,在编译阶段优化内存存取的一项技术。

说白了,就是各种类型的数据按照“一定的规则”在内存空间上排列,而不是顺序的一个接一个排列,如果当前的内存地址不满足规则就跳过,空着不用,直到满足规则的内存地址出现才存储。

为什么要进行内存对齐?

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

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

计算机CPU一次可以处理多个字节,就拿32位系统来说,CPUT一次可以处理32bit的数据,也就是4个字节。假设平台每次都从偶地址开始读取数据,有一个int型数据,存放在内存地址0x1的位置。CPU要读取这个int数据,需要先从地址0x0开始读取4字节数据,此时这个int型还有一个字节没有读到,就得再从地址0x4开始读取4字节数据,并且还要进行位操作,把两次读取的数据合并为一个int型数据。多次读取,还要移位!—— 太麻烦,效率又低。那怎么办呢?为了简单高效地读取数据,干脆在存储时把这个int数据放在地址0x4的位置,0x1、0x2、0x3的位置都空着,CPU直接从0x4取数据,只需一次就取到了这个数据,还不用进行位操作。简而言之,就是拿空间换时间。

字节对齐规则

1.内置基础数据类型对齐模数(有符号无符号相同)

存对齐对基础数据类型在内存中存放的位置进行了限制,要求这些数据的首地址必须是某个数N的倍数,N称为该数据类型的对齐模数(alignment modulus)。

7dffc819ae00e7a2edbae20f07ffa991.png

2.指定对齐模数

我们给编译器指定的对齐模数(在VC中使用指令:#pragma pack(n),如果不指定,在VS2010默认为8)

3.有效对齐模数

指定对齐模数与类型自身对齐模数的较小的值,就是实际生效的对齐模数

4.自定义类型的对齐模数(struct、class)

自定义类型(如结构)的对齐模数等同于其成员中最大的有效对齐模数,且具有一下特点:

各个成员按照被声明的顺序在内存中顺序(升序)存储,第一个成员的地址和整个类型的地址相同;

每个成员按照自身的有效对齐模数分别对齐;

结构体长度必须是结构体对齐模数的整数倍,末尾不够的地方补齐空字节。

例如,

struct test_A {

char ch1; // 自身对齐模数1,指定对齐模数8,有效对齐模数1

char ch2; // 自身对齐模数1,指定对齐模数8,有效对齐模数1

int i1; // 自身对齐模数4,指定对齐模数8,有效对齐模数4

short sh1; // 自身对齐模数2,指定对齐模数8,有效对齐模数2

} st_test_A; // 自身对齐模数4,指定对齐模数8,有效对齐模数4

struct test_B {

char ch1;

char ch2;

short sh1;

int i1;

} st_test_B;

struct test_C {

char ch1;

char ch2;

char ch3;

} st_test_C[2];

printf("sizeof(test_A) = %d, addr st_test_A = %p\n", sizeof(test_A), &st_test_A);

printf(" sizeof(char) = %d, addr ch1 = %p\n", sizeof(char), &st_test_A.ch1);

printf(" sizeof(char) = %d, addr ch2 = %p\n", sizeof(char), &st_test_A.ch2);

printf(" sizeof(int) = %d, addr i1 = %p\n", sizeof(int), &st_test_A.i1);

printf(" sizeof(short) = %d, addr sh1 = %p\n\n", sizeof(short), &st_test_A.sh1);

printf("sizeof(test_B) = %d, addr st_test_B = %p\n", sizeof(test_B), &st_test_B);

printf(" sizeof(char) = %d, addr ch1 = %p\n", sizeof(char), &st_test_B.ch1);

printf(" sizeof(char) = %d, addr ch2 = %p\n", sizeof(char), &st_test_B.ch2);

printf(" sizeof(short) = %d, addr sh1 = %p\n", sizeof(short), &st_test_B.sh1);

printf(" sizeof(int) = %d, addr i1 = %p\n\n", sizeof(int), &st_test_B.i1);

printf("sizeof(test_C) = %d, addr st_test_C[0] = %p\n", sizeof(test_C), &st_test_C[0]);

printf(" sizeof(char) = %d, addr ch1 = %p\n", sizeof(char), &st_test_C[0].ch1);

printf(" sizeof(char) = %d, addr ch2 = %p\n", sizeof(char), &st_test_C[0].ch2);

printf(" sizeof(char) = %d, addr ch3 = %p\n", sizeof(char), &st_test_C[0].ch3);

printf("sizeof(test_C) = %d, addr st_test_C[1] = %p\n", sizeof(test_C), &st_test_C[1]);

printf(" sizeof(char) = %d, addr ch1 = %p\n", sizeof(char), &st_test_C[1].ch1);

printf(" sizeof(char) = %d, addr ch2 = %p\n", sizeof(char), &st_test_C[1].ch2);

printf(" sizeof(char) = %d, addr ch3 = %p\n\n", sizeof(char), &st_test_C[1].ch3);

在上面的例子中,如下图所示,st_test_A的起始地址为0x008FF9D0,是能被ch1的有效对齐模数1整除的地址,所以ch1存储在该地址,ch2同理;0x008FF9D2和0x008FF9D3都不能被i1的有效对齐模数4整除,因此不能存储i4,需要跳过,直到0x008FF9D4能被4整除,在该地址上存储i1。以此类推,在0x008FF9D8存储完最后一个成员sh1后,结构体的长度为10,不满足结构体对齐模数4的整数倍,因此需要在末尾在补上2个空字节。最终,st_test_A的长度为12。

31bf0a77695303a59855c59fa7a6b69c.png

从上面的例子我们不难发现,虽然st_test_A和st_test_B包含了一样的成员变量,但是两者的长度sizeof()却是不一样的。可见成员变量合理的声明顺序可以避免空字节的产生,节约存储空间。

VC中的字节对齐设置

在VS IDE中,可以这样修改:[Project]|[Settings],c/c++选项卡Category的Code Generation选项的Struct Member Alignment中修改,Default是8字节。

在编码时,可以使用指令动态修改:#pragma pack

#pragma pack(4) // 指定按照4字节对齐,缺省为8字节对齐

// ...

#pragma pack() // 回复默认字节对齐,即8字节对齐

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值