字节对齐

字节对齐

标签: 嵌入式开发

1、为什么要字节对齐

结论:为了提高数据传递的效率。

芯片内核和存储芯片之间通过控制总线来控制存储芯片的行为,通过地址总线告诉存储芯片地址,通过数据总线来传递数据。

假设8位机,即数据总线是8bits,假设地址总线也是8bits。
考察程序1:

char test;
test='a';
test++;

第一句定义char类型变量test,编译器会给test分配一个内存地址,假设是0x10。
第二句,向0x10这个地址写入’a’对应的ASCII码。
第三句,从0x10这个地址读出数据,加1后写回0x10。
一个指令周期就可以完成读或者写操作。

考察程序2:

int num;
num=1;
num++;

第一句定义int类型变量num,编译器会给num分配内存地址。假设int 2个字节长度,那么假设分配的地址就是0x10,0x11。
第二句,假设我们采用大端模式存储,地址0x10写入0x00,地址0x11写入0x01。
第三句,数据总线第一次读出地址0x10的数据0x00,放到寄存器R0;第二次读出地址0x11的数据0x01,放到寄存器R1;然后R1+1;并将R0、R1的值写回地址0x10、0x11。
因为这是个8位机,数据总线8bits,一次只能读一个字节的数据,所以读或者写int类型的数据的话需要两个指令周期。

再来看看16位机,数据总线16bits,假设地址总线也是16bits。
地址总线多了,可寻址的空间大了;数据总线多了,一个字节还是8bits,剩下的8bits的数据线也不能闲着,来传递下一个地址的8bits数据。
对于考察程序1,假设分配到0x0010地址,情况和8位机类似,一个指令周期就可以完成对char类型数据的读或者写工作。
对于考察程序2,假设分配到0x0010,0x0011地址,0x0010存放0x00,0x0011存放0x01。当对int数据进行读取的时候,地址总线是0x0010,数据总线读的是地址0x0010的8bits数据外加地址0x0011的8bits数据。所以一个指令周期就可以完成对16bits的数据读或者写。
对于考察程序2,如果编译器给分配地址是0x0011和0x0012怎么办?那么如果想读取这个int类型的数据怎么办?对不起,得两个指令周期:首先,地址总线写0x0010,数据总线读取地址0x0010和地址0x0011;然后,地址总线写0x0012,数据总线读取地址0x0012和地址0x0013。然后我们要的是0x0011和0x0012的数据。
如果我还想一个指令周期读这些数据怎么办?那就需要字节对齐:对于int型数据,分配内存空间的时候要分配到0x0010、0x0012这种地址上面,不能分配到0x0011、0x0013这种地址上面,也就是分配到2的整数倍的地址上面。

对于32位机,也是类似的情形。一次可以访问4个字节的数据。地址总线上面写0x00000000,数据总线会把0x00000000,0x00000001,0x00000002,0x00000003一共32bits的数据读回来。所以如果有一个4字节的数据,那么最好存储的时候4字节对齐,也就是分配到4的整数倍的地址上面。
可能有人会想,我地址总线上面写0x00000001不行么,这样的话不就可以访问从01到04的数据,就可以不对齐了么?但这个是由硬件决定的,硬件上不允许这么写。

同理,对于64位机,需要8字节对齐。

2、字节对齐规则

一般在编写程序的时候,不会指定变量存放在内存中的地址,因为编译器连接器会遵循字节对齐的原则,分配好地址。以32位机为例,1字节长度的变量分配到1的整数倍的地址,2字节长度的变量分配到2的整数倍的地址,4字节长度的变量分配到4的整数倍的地址。
共用体union的长度是里面最大的变量长度的整数倍。例如:

union tagA{
    char first;
    int second;
    long third;
}examp;

那么union的分配长度就是long的长度。
共用体很穷,舍不得给每个成员分配地方,所以按照最大需要的来分配,其他的成员就先将就用。

结构体每个成员都会给分配空间。

struct tagA{
    char first;
    int second;
    long third;
}examp;

那么分配的长度就是char+int+long。

3、例子

例1

union tagAAA
{
    struct{
        char ucFirst;
        short usSecond;
        char ucThird;
    }half;
    long lI;
}number;

字节对齐为1,对于union中的结构体half,长度为char+short+char=1+2+1=4;long长度也为4;按照最大长度来分配,sizeof(union tagAAA)=4。
相应的测试代码如下:

#include <stdio.h>

#pragma pack(1)
union tagAAA
{
    struct{
        char ucFirst;
        short usSecond;
        char ucThird;
    }half;
    long lI;
}number;

int main(void)
{
    printf("size of number is %d\n",sizeof(number));

    getchar();
    return 0;
}

运行结果为4.

4字节对齐,对于half,假设char ucFirst1个字节分配地址0,short需要分配两个字节并且需要2的整数倍地址,只能是地址2和地址3(地址1就没有使用),char ucThird一个字节分配地址5,所以half一共需要5个地址,但是是四字节对齐的,要分配就给4的整数倍。我需要5个字节,所以要分配8个字节。
对于long,需要4个字节。本着union满足最大需要分配的前提,union tagAAA分配8个字节。

类比着住旅馆,每个房间有1张床,并且旅馆规定只能订连号的房间,并且必须订4的整数间房子,起始房间号必须是4的倍数。也就是说,你可以一间都不订,要订就得是4间、8间、12间这么订,并且房间号必须是从0、4、8······这些房间开始。
现在来了一家三口,丈夫说我睡一张床就行,孩子说我要两张床,我住一间,另一间放玩具,并且我要住偶数号的房间,妻子说我也住一张床就行。丈夫住0号房间,孩子要住偶数房间,所以住2号,玩具放3号,1号房间没人住,也要包下来。妻子住4号房间。这样一共是五间房子,前台说我们只租4的整数倍,所以需要八间房子。类比的不是很恰当,领会意思即可。
相应的测试代码如下:

#include <stdio.h>

union tagAAA
{
    struct{
        char ucFirst;
        short usSecond;
        char ucThird;
    }half;
    long lI;
}number;

int main(void)
{
    printf("size of number is %d\n",sizeof(number));

    getchar();
    return 0;
}

运行结果为8.

例2

struct tagBBB
{
    char ucFirst;
    short usSecond;
    char ucThird;
    short usForth;
}half;

1字节对齐,char+short+char+short=1+2+1+2=6。
测试代码:

#include <stdio.h>

#pragma pack(1)
struct tagBBB
{
    char ucFirst;
    short usSecond;
    char ucThird;
    short usForth;
}half;

int main(void)
{
    printf("size of half is %d\n",sizeof(half));

    getchar();
    return 0;
}

4字节对齐,char ucFirst分配到地址0,short usSecond需要2的倍数地址,分配到地址2、3,char ucThird分配到地址4,usForth分配到6、7。一共需要8个字节。
测试代码:

#include <stdio.h>

struct tagBBB
{
    char ucFirst;
    short usSecond;
    char ucThird;
    short usForth;
}half;

int main(void)
{
    printf("size of half is %d\n",sizeof(half));

    getchar();
    return 0;
}

例3

struct tagCCC
{
    struct 
    {
        char ucFirst;
        short usSecond;
        char ucThird;
    }half;
    long lI;
};

1字节对齐,对于half,char+short+char=1+2+1=4,long=4,所以一共需要8个字节。
4字节对齐,对于half,char ucFirst分配地址0,short usSecond分配2的整数倍地址,分配地址2、3,ucThird分配地址4,所以half需要8个字节的地址(详细说明参见住宾馆的那个例子)。对于long,需要4个字节,所以一共需要12个字节。
测试代码和上面类似,只不过改一下结构体就可以,这里就不给出了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值