大小端问题是一个很简单的概念,但在实际使用中却是一个所必须考虑的非常重要的细节。虽然简单却也曾经困扰过我很多次,今天正好有空整理出来做个笔记。
字节序的大小端通常指的是在一个字内(32位或者64位)各字节在内存中的存放顺序。注意这里仅是一个字内Byte的顺序,各个字之间的顺序不受大小端的影响。另外还要注意想一个int数据进行左右移位是跟大小端没关系的,因为编译器已经屏蔽了这层差异,不要混淆。另外值得注意的一点是,字节内的bit位也是有大小端区分的,这个在通信协议中需要重点关注。
1)在Big Endian的情况下,"排在前面的是高位"
a. 对于顺序的两个字节来说,第一个字节是高位(排在前面),第二个字节是低位(排在后面)。
b. 对于字节内部的位来说,
-------most significant bits排在前面,是高位,
-------least significant bits排在后面,是低位。
2)在Little Endian的情况下,"排在前面的是低位"
a. 对于顺序的两个字节来说,第一个字节是低位(排在前面),第二个字节是高位(排在后面)。
b. 对于字节内部的位来说,
-------least significant bits排在前面,是低位,
-------most significant bits排在后面,是高位。
大端中一个字内的高有效位在低地址,低有效位在高地址;小端正好相反。
下面以0x12345678,在大端情况下在内存的存放为:
内存地址 : 0x10000000 0x10000001 0x10000002 0x10000003
大端: 0x12 0x34 0x56 0x78
小端: 0x78 0x56 0x34 0x12
可见小端正好和我们日常的书写顺序相反,助记口诀----小反(三国杀中的反贼角色)。
在实际编码中,我们经常需要将一个buffer数组转换成大小端相关的uint32。来看一个例子:
在实际使用中多是,主机字节序与网络字节序(或者寄存器读出来的数据)之间的转换,转换类型有三种:
1、U32或者U16字节序转换;
2、UCHAR数组和UINT32之间的相互转换;
3、结构体中位域的大小端(参见我的另外一篇博客--位域与大小端);
下面是几个例子:
1.1 判 断大小端代码
judge_endian()
{
uint a= 1;
if (*((char *)&a))
printf(“BIGEndian\r\n”);
}
1.1.2 UINT字节序转换
htonl的意思是 host to network long
#if defined(LE_HOST)
#define htonl(_l) _ swap32(_l)
#define htons(_s) _ wap16(_s)
#define ntohl(_l) _ swap32(_l)
#define ntohs(_s) _ swap16(_s)
#else
#define htonl(_l) (_l)
#define htons(_s) (_s)
#define ntohl(_l) (_l)
#define ntohs(_s) (_s)
#endif
unsigned int _swap32(unsigned int i)
{
i = (i << 16) | (i >> 16);
return (i & 0xff00ffff) >> 8 | (i & 0xffff00ff) <<8;
}
unsigned short _swap16(unsigned short i)
{
return i << 8 | i >> 8;
}
1.1.3 UINT与char数组字节序转换
字-->char数组
chipc_spi_read(uint8 * buf, int len)
{
/* chipc_spi_fifo_readword读取寄存器,返回uint类型*/
/*将获取到的value值根据字节序转换到char数组中去*/
#if defined(BE_HOST)
value = 0;
for (idx =0 ; idx < len ; idx++) {
m = idx% 4;
if (m== 0) {
value = chipc_spi_fifo_readword();
}
*buf++= (uint8)(value >> ((3-m)*8));
}
#else
value = 0;
for (idx =0 ; idx < len ; idx++) {
m = idx% 4;
if (m== 0) {
value = chipc_spi_fifo_ readword ();
}
*buf++= (uint8)(value >> (m*8));
}
#endif
}
char数组-->字
chipc_spi_write_fifo(uint8 *data, int len)
{
/*将char数组的值转换成uint,然后通过chipc_spi_fifo_writeword函数写到寄存器里去*/
#if defined(BE_HOST)
value = 0;
for (idx =0 ; idx < len ; idx++) {
m = idx% 4;
value|= (*data << ((3-m)*8));
data++;
if ((m== 3) || (idx == (len - 1))) {
chipc_spi_fifo_writeword(value);
value = 0;
}
}
#else
value = 0;
for (idx =0 ; idx < len ; idx++) {
m = idx% 4;
value|= (*data << (m*8));
data++;
if ((m== 3) || (idx == (len - 1))) {
chipc_spi_fifo_writeword(value);
value = 0;
}
}
#endif
}