背景
在不同的计算机系统中,数据的存储和读取有所不同,计算机的通信和存储依赖于一致的规则。目前计算机通常采用的存储机制主要有两种:大端模式(Big-endian)和小端模式(Little-endian)
大下端常规定义
大端模式:数据的低位或低字节(对整个序列取值影响最小的那个bit/byte)保存在内存的高地址中,而数据的高位或高字节,存储时放在低地址,读取时放在流开始。这样的存储模式有点儿类似于把数据当作字符串顺序处理:地址由小向大增加,而数据从高位往低位放,存储顺序与我们所看到的数据顺序一致;
小端模式:数据的低位或低字节,存储时放在低地址,读取时放在流的末尾,而数据的高位或高字节保存在内存的高地址中。
关于大小端更深刻更容易记忆的定义请见我的另外一篇博客:大小端如何理解记忆
大小端转换示例
在通信中,读写寄存器地址及数据流有规定的大小端格式,需要按照规定的格式来通信。
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#define BUF_LEN ( 4 )
static void endian_exchange(uint8_t *buf, uint32_t len);
static void Fbytes_endian_exchange(uint32_t *buf, uint32_t len); // four bytes endian exchange
static void endian_exchange(uint8_t *buf, uint32_t len) {
uint32_t size = len & 0xFFFFFFFE; // 长度取偶数操作
uint32_t i = 0;
uint8_t buf_tmp = 0;
if (NULL == buf) {
printf("[%s] buf = %p len = %u", __func__, buf , len);
return;
}
for (i = 0; i < size; i += 2) {
buf_tmp = buf[i + 1];
buf[i + 1] = buf[i];
buf[i] = buf_tmp;
}
}
static void Fbytes_endian_exchange(uint32_t *buf, uint32_t len) {
uint32_t i = 0;
if (NULL == buf || len < 3 || (0 != len % 4)) {
printf("[%s] buf = %p len = %u", __func__, buf , len);
return;
}
for (i = 0; i < len / 4; i++) {
buf[i] = ((buf[i] & 0x00FF00FF) << 8) | ((buf[i] >> 8) & 0x00FF00FF);
buf[i] = ((buf[i] & 0x0000FFFF) << 16) | ((buf[i] >> 16) & 0x0000FFFF);
}
}
int main(void) {
uint8_t i = 0;
uint8_t buf_8[BUF_LEN] = {0x01, 0x23, 0x45, 0x67};
uint32_t buf_32 = 0x01234567;
for (i = 0; i < BUF_LEN; i++) {
printf("[%s] buf_8[%u] = 0x%02x \n", __func__, i, buf_8[i]);
}
printf("[%s] buf_32 = 0x%x \n", __func__, buf_32);
endian_exchange(buf_8, BUF_LEN * sizeof(buf_8[0]));
for (i = 0; i < BUF_LEN; i++) {
printf("[%s] endian_exchange buf_8[%u] = 0x%02x \n", __func__, i, buf_8[i]);
}
Fbytes_endian_exchange(&buf_32, sizeof(buf_32));
printf("[%s] Fbytes_endian_exchange buf_32 = 0x%x \n", __func__, buf_32);
return 0;
}
上述示例分别完成了16位和32位大小端转换(即2字节和4字节),结果如下:
[main] buf_8[0] = 0x01
[main] buf_8[1] = 0x23
[main] buf_8[2] = 0x45
[main] buf_8[3] = 0x67
[main] buf_32 = 0x1234567
[main] endian_exchange buf_8[0] = 0x23
[main] endian_exchange buf_8[1] = 0x01
[main] endian_exchange buf_8[2] = 0x67
[main] endian_exchange buf_8[3] = 0x45
[main] Fbytes_endian_exchange buf_32 = 0x67452301
可以看出:
- 16位大小端转换是将每16位数据的高低字节调换存储位置
- 32位大小端转换是先将每16位数据的高低字节调换存储位置,再将每相邻的两个字节调换位置