当心!别再被大小端的问题坑了
先说内存
柿子捡软的捏,以前做项目的时候被大小端的问题坑过,那种酸爽就像蓝天白云,晴空万里忽然暴风雨,突如其来的BUG,让原本不充裕的时间更加雪上加霜;虽然很基础,但是能力有限,也难免出现错误和纰漏,请各位大佬们在讨论中无情指正我。
先说内存
程序运行在内存中,计算机中的最小存储单位是Bit
,即1
和0
的二进制,它可以识别的机器码就是以二进制形式存储的;
内存由多个存储单元组成,每个存储单元都有一个唯一的数字地址字节可寻址内存。每个存储位置可以包含固定数量的二进制数字。
在大多数的现代计算机上,地址的最小数据的长度为8
位,称为字节(1 Byte = 8 Bit
);
一般计算机中用户程序直接访问的地址是虚拟内存的地址,操作系统内核会根据用户程序访问的虚拟地址,找出页表中对于的物理地址,最终寻址到所需要的数据;
具体如下图所示;
然而,在MCU等裸机开发的环境中,没有MMU
,则程序直接访问的是物理内存,所以无论是计算机还是MCU在程序运行中都需要内存作为载体,保存数据和运行程序。
那么,下面再来看是程序以及数据在内存中是以何种形式存储的?
字节
前面提到过,在大多数的现代计算机上,地址的最小数据的长度为8
位,称为字节(1 Byte = 8 Bit
);至于为什么是8位?看起来似乎有点玄学,并且很吉利的一个数字,但是老外好像没有数字迷信,这里的原因大概是因为这几点;
由于计算机内部最本质需要实现的操作是加法,减法,乘法,除法等运算都能通过加法实现,另外由于最早期设计的加法器是8位;
另外一个原因可以追溯到1956年,IBM公司最早提出字节的概念,随着IBM的壮大,字节便专门用来表示二进制数,其中也包括不少优点;易于以BCD码形式保存;用于保存文本也非常合适,另外世界上大部分语言都可以用小于256个字符(一个字节宽度)来表示,如果一个不够,那就两个,比如中文;
字节顺序
在说大小端之前,要先提一下字节顺序(Endianness
),它是描述数据以字节为一组在计算机内存中存储顺序的术语。
字节顺序可以是大端顺序(big-endian
)或者小端顺序(little-endian
);在对多字节数据进行存储时,一般遵循以下规则;
小端:数据的最后一个字节先存储,即LSB; 大端:数据的第一个字节先存储,即MSB;
数据0x01020304
分别在大端机器和小端机器中的存储形式,具体如下图所示;
在大多数情况下,编译器会处理字节顺序,从而避免出现大小端不一致的问题,但是在以下情况下字节顺序就会成为一个问题。
在通讯中,例如网络编程:假设在小端机器上向文件写入整数,然后将此文件传输到大端机器上。如果没有做大小端转换,那么大端机器就会以相反的顺序读取文件。
TCP/IP
协议中,默认使用的是大端顺序,它与具体的CPU类型、操作系统等无关;
那么如何在程序中快速的区分大小端呢?
大端和小端的区分
下面介绍几种通过C语言实现大小端判断的方法;
第一种通过指针的内存对齐来实现;
函数的形式;
unsigned char check_endian( void )
{
int test_var = 1;
unsigned char *test_endian = (unsigned char*)&test_var;
return (test_endian[0] == 0);
}
宏定义的形式;
static uint32_t endianness = 0xdeadbeef;
enum endianness { BIG, LITTLE };
#define ENDIANNESS ( *(const char *)&endianness == 0xef ? LITTLE \
: *(const char *)&endianness == 0xde ? BIG \
: assert(0))
更加简洁;
#define IS_BIG_ENDIAN (!*(unsigned char *)&(uint16_t){1})
第二种通过结构体和联合体的内存对齐来实现;
#ifndef ORDER32_H
#define ORDER32_H
#include <limits.h>
#include <stdint.h>
#if CHAR_BIT != 8
#error "unsupported char size"
#endif
enum
{
O32_LITTLE_ENDIAN = 0x03020100ul,
O32_BIG_ENDIAN = 0x00010203ul,
O32_PDP_ENDIAN = 0x01000302ul, /* DEC PDP-11 (aka ENDIAN_LITTLE_WORD) */
O32_HONEYWELL_ENDIAN = 0x02030001ul /* Honeywell 316 (aka ENDIAN_BIG_WORD) */
};
static const union { unsigned char bytes[4]; uint32_t value; } o32_host_order =
{ { 0, 1, 2, 3 } };
#define O32_HOST_ORDER (o32_host_order.value)
#endif
当然具体的方法还有很多,本文就先讲到这里。
—— The End ——
长按识别二维码关注获取更多内容