关于CPU的大小端存储模式
1. 决定因素
CPU是大小端存储的决定因素,编译器和操作系统仅仅是为了适配CPU。
2. CPU为什么会有大小端存储模式
在计算机中,数据存储的基本单位(也叫最小单位)是字节,每个存储单元(一个地址单元)上可以存储一个字节的数据。以C语言为例,char类型的数据就是一个字节,但是其他的数据类型就不止一个字节,比如int类型的数据是4个字节,short类型(short int)是2字节,long类型(long int)是8/4字节(与编译器的数据模型相关),long long数据类型有8字节……。也就是说,当一个数据大于8个bit的时候,CPU要如何进行存储。
可以认为内存地址是有序递增的(低地址到高地址),所以进行数据存储的时候无非就两种情况,低地址上放低字节数据还是放高字节数据。
3. 大小端存储模式的概念
大端模式(Big Endian):数据的高字节存储在内存的低地址中
小端模式(Little Endian):数据的高字节存储在内存的高地址中
例子:CPU/MCU 存储一个整型数据 int data = 305419896 (int类型的最大值是4294967295)
为了形象一点,先将10进制数据转成16进制,即305419896D = 12345678H。对于数据0x12345678来说,高字节数据到低字节数据分别是:0x12、 0x34、 0x56、 0x78,总共是4个字节。
(1)如果CPU是大端模式,则数据存储如下图。
假设存储的起始地址为0x00001f00,则存储4字节需要用到4个连续的地址单元,地址单元和地址上存储的字节数据对应如下:
Address Data 0x00001f00 0x12 0x00001f01 0x34 0x00001f02 0x56 0x00001f03 0x78 |
细分到0/1存储:
Address: 0x00001f00 0x00001f01 0x00001f02 0x00001f03 Data : 0001 0010 0011 0100 0101 0110 0111 1000 |
(2)如果CPU是小端模式,则数据存储如下图。
假设存储的起始地址为0x00001f00,则存储4字节需要用到4个连续的地址单元,地址单元和地址上存储的字节数据如下:
Address Data 0x00001f00 0x78 0x00001f01 0x56 0x00001f02 0x34 0x00001f03 0x12 |
细分到0/1存储:
Address: 0x00001f00 0x00001f01 0x00001f02 0x00001f03 Data : 0111 1000 0101 0110 0011 0100 0001 0010 |
4. 判断方法
方法1:使用共用体
共用体中的所有数据成员共用同一块内存(连续的多个单元),即各个数据成员存储的起始地址相同。
测试程序如下:
union cpu_mode { int a; /*4 byte*/ char b; /*1 byte*/ char c; /*1 byte*/ char d; /*1 byte*/ char e; /*1 byte*/ } pri_data; pri_data.a = 1; printk("pri_data.a = %d\n", pri_data.a);
printk("pri_data.b = %d\n", pri_data.b); printk("pri_data.c = %d\n", pri_data.c); printk("pri_data.d = %d\n", pri_data.d); printk("pri_data.e = %d\n", pri_data.e);
if (1 == pri_data.b == pri_data.c == pri_data.d == pri_data.e) { printk("小端存储模式\n"); } else { printk("大端存储模式\n"); } |
对于数据成员a,值为0x00 00 00 01,如果CPU是小端模式,则低地址上存储的值应该是0x01,读共用体的其他数据成员的值就应该读到1;反之,如果CPU是大端模式,则低地址上存储的就是0x00,其他数据成员的值应该是0。
方法2:直接读取内存地址上的值,测试程序如下。
unsigned short pri_data=0xaabb;
char data1,data2;
data1 = *((char*)&pri_data); //低地址单元上的数据 data2 = *((char*)&pri_data + 1); //高地址单元上的数据
printk("data1 = 0x%02x\n", data1); printk("data2 = 0x%02x\n", data2);
if (data1 == 0xbb && data2 == 0xaa) { printk("小端存储模式\n");
} else if (data1 == 0xaa && data2 == 0xbb) { printk("大端存储模式\n");
} else { printk("未知存储模式\n");
} |